aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/optimizer/plan/subselect.c1
-rw-r--r--src/backend/parser/parse_oper.c36
-rw-r--r--src/backend/utils/cache/lsyscache.c61
-rw-r--r--src/backend/utils/cache/typcache.c345
-rw-r--r--src/include/catalog/pg_operator.h3
-rw-r--r--src/include/utils/typcache.h11
-rw-r--r--src/test/regress/expected/rowtypes.out10
-rw-r--r--src/test/regress/sql/rowtypes.sql7
8 files changed, 348 insertions, 126 deletions
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 45eaa03fda7..71ffc17b599 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -891,6 +891,7 @@ hash_ok_operator(OpExpr *expr)
if (opid == ARRAY_EQ_OP)
{
/* array_eq is strict, but must check input type to ensure hashable */
+ /* XXX record_eq will need same treatment when it becomes hashable */
Node *leftarg = linitial(expr->args);
return op_hashjoinable(opid, exprType(leftarg));
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index 15a3bb3a013..4a2f77771b8 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -211,42 +211,6 @@ get_sort_group_operators(Oid argtype,
gt_opr = typentry->gt_opr;
hashable = OidIsValid(typentry->hash_proc);
- /*
- * If the datatype is an array, then we can use array_lt and friends ...
- * but only if there are suitable operators for the element type.
- * Likewise, array types are only hashable if the element type is. Testing
- * all three operator IDs here should be redundant, but let's do it
- * anyway.
- */
- if (lt_opr == ARRAY_LT_OP ||
- eq_opr == ARRAY_EQ_OP ||
- gt_opr == ARRAY_GT_OP)
- {
- Oid elem_type = get_base_element_type(argtype);
-
- if (OidIsValid(elem_type))
- {
- typentry = lookup_type_cache(elem_type, cache_flags);
- if (!OidIsValid(typentry->eq_opr))
- {
- /* element type is neither sortable nor hashable */
- lt_opr = eq_opr = gt_opr = InvalidOid;
- }
- else if (!OidIsValid(typentry->lt_opr) ||
- !OidIsValid(typentry->gt_opr))
- {
- /* element type is hashable but not sortable */
- lt_opr = gt_opr = InvalidOid;
- }
- hashable = OidIsValid(typentry->hash_proc);
- }
- else
- {
- lt_opr = eq_opr = gt_opr = InvalidOid; /* bogus array type? */
- hashable = false;
- }
- }
-
/* Report errors if needed */
if ((needLT && !OidIsValid(lt_opr)) ||
(needGT && !OidIsValid(gt_opr)))
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index d3b2a5a5572..28d18b0d32d 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -33,6 +33,7 @@
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/datum.h"
+#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
@@ -1120,34 +1121,35 @@ op_input_types(Oid opno, Oid *lefttype, Oid *righttype)
* opfamily entries for this operator and associated sortops. The pg_operator
* flag is just a hint to tell the planner whether to bother looking.)
*
- * In some cases (currently only array_eq), mergejoinability depends on the
- * specific input data type the operator is invoked for, so that must be
- * passed as well. We currently assume that only one input's type is needed
- * to check this --- by convention, pass the left input's data type.
+ * In some cases (currently only array_eq and record_eq), mergejoinability
+ * depends on the specific input data type the operator is invoked for, so
+ * that must be passed as well. We currently assume that only one input's type
+ * is needed to check this --- by convention, pass the left input's data type.
*/
bool
op_mergejoinable(Oid opno, Oid inputtype)
{
- HeapTuple tp;
bool result = false;
+ HeapTuple tp;
+ TypeCacheEntry *typentry;
+ /*
+ * For array_eq or record_eq, we can sort if the element or field types
+ * are all sortable. We could implement all the checks for that here, but
+ * the typcache already does that and caches the results too, so let's
+ * rely on the typcache.
+ */
if (opno == ARRAY_EQ_OP)
{
- /*
- * For array_eq, can sort if element type has a default btree opclass.
- * We could use GetDefaultOpClass, but that's fairly expensive and not
- * cached, so let's use the typcache instead.
- */
- Oid elem_type = get_base_element_type(inputtype);
-
- if (OidIsValid(elem_type))
- {
- TypeCacheEntry *typentry;
-
- typentry = lookup_type_cache(elem_type, TYPECACHE_BTREE_OPFAMILY);
- if (OidIsValid(typentry->btree_opf))
- result = true;
- }
+ typentry = lookup_type_cache(inputtype, TYPECACHE_CMP_PROC);
+ if (typentry->cmp_proc == F_BTARRAYCMP)
+ result = true;
+ }
+ else if (opno == RECORD_EQ_OP)
+ {
+ typentry = lookup_type_cache(inputtype, TYPECACHE_CMP_PROC);
+ if (typentry->cmp_proc == F_BTRECORDCMP)
+ result = true;
}
else
{
@@ -1178,22 +1180,17 @@ op_mergejoinable(Oid opno, Oid inputtype)
bool
op_hashjoinable(Oid opno, Oid inputtype)
{
- HeapTuple tp;
bool result = false;
+ HeapTuple tp;
+ TypeCacheEntry *typentry;
+ /* As in op_mergejoinable, let the typcache handle the hard cases */
+ /* Eventually we'll need a similar case for record_eq ... */
if (opno == ARRAY_EQ_OP)
{
- /* For array_eq, can hash if element type has a default hash opclass */
- Oid elem_type = get_base_element_type(inputtype);
-
- if (OidIsValid(elem_type))
- {
- TypeCacheEntry *typentry;
-
- typentry = lookup_type_cache(elem_type, TYPECACHE_HASH_OPFAMILY);
- if (OidIsValid(typentry->hash_opf))
- result = true;
- }
+ typentry = lookup_type_cache(inputtype, TYPECACHE_HASH_PROC);
+ if (typentry->hash_proc == F_HASH_ARRAY)
+ result = true;
}
else
{
diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c
index 2769a30acd8..14d9cf7a445 100644
--- a/src/backend/utils/cache/typcache.c
+++ b/src/backend/utils/cache/typcache.c
@@ -10,11 +10,11 @@
* be used for grouping and sorting the type (GROUP BY, ORDER BY ASC/DESC).
*
* Several seemingly-odd choices have been made to support use of the type
- * cache by the generic array handling routines array_eq(), array_cmp(),
- * and hash_array(). Because those routines are used as index support
- * operations, they cannot leak memory. To allow them to execute efficiently,
- * all information that they would like to re-use across calls is kept in the
- * type cache.
+ * cache by generic array and record handling routines, such as array_eq(),
+ * record_cmp(), and hash_array(). Because those routines are used as index
+ * support operations, they cannot leak memory. To allow them to execute
+ * efficiently, all information that they would like to re-use across calls
+ * is kept in the type cache.
*
* Once created, a type cache entry lives as long as the backend does, so
* there is no need for a call to release a cache entry. (For present uses,
@@ -28,8 +28,9 @@
* doesn't cope with opclasses changing under it, either, so this seems
* a low-priority problem.
*
- * We do support clearing the tuple descriptor part of a rowtype's cache
- * entry, since that may need to change as a consequence of ALTER TABLE.
+ * We do support clearing the tuple descriptor and operator/function parts
+ * of a rowtype's cache entry, since those may need to change as a consequence
+ * of ALTER TABLE.
*
*
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
@@ -49,6 +50,7 @@
#include "access/nbtree.h"
#include "catalog/indexing.h"
#include "catalog/pg_enum.h"
+#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "utils/builtins.h"
@@ -65,6 +67,15 @@
/* The main type cache hashtable searched by lookup_type_cache */
static HTAB *TypeCacheHash = NULL;
+/* Private flag bits in the TypeCacheEntry.flags field */
+#define TCFLAGS_CHECKED_ELEM_PROPERTIES 0x0001
+#define TCFLAGS_HAVE_ELEM_EQUALITY 0x0002
+#define TCFLAGS_HAVE_ELEM_COMPARE 0x0004
+#define TCFLAGS_HAVE_ELEM_HASHING 0x0008
+#define TCFLAGS_CHECKED_FIELD_PROPERTIES 0x0010
+#define TCFLAGS_HAVE_FIELD_EQUALITY 0x0020
+#define TCFLAGS_HAVE_FIELD_COMPARE 0x0040
+
/* Private information to support comparisons of enum values */
typedef struct
{
@@ -109,6 +120,14 @@ static TupleDesc *RecordCacheArray = NULL;
static int32 RecordCacheArrayLen = 0; /* allocated length of array */
static int32 NextRecordTypmod = 0; /* number of entries used */
+static void load_typcache_tupdesc(TypeCacheEntry *typentry);
+static bool array_element_has_equality(TypeCacheEntry *typentry);
+static bool array_element_has_compare(TypeCacheEntry *typentry);
+static bool array_element_has_hashing(TypeCacheEntry *typentry);
+static void cache_array_element_properties(TypeCacheEntry *typentry);
+static bool record_fields_have_equality(TypeCacheEntry *typentry);
+static bool record_fields_have_compare(TypeCacheEntry *typentry);
+static void cache_record_field_properties(TypeCacheEntry *typentry);
static void TypeCacheRelCallback(Datum arg, Oid relid);
static void load_enum_cache_data(TypeCacheEntry *tcache);
static EnumItem *find_enumitem(TypeCacheEnumData *enumdata, Oid arg);
@@ -257,17 +276,34 @@ lookup_type_cache(Oid type_id, int flags)
if ((flags & (TYPECACHE_EQ_OPR | TYPECACHE_EQ_OPR_FINFO)) &&
typentry->eq_opr == InvalidOid)
{
+ Oid eq_opr = InvalidOid;
+
if (typentry->btree_opf != InvalidOid)
- typentry->eq_opr = get_opfamily_member(typentry->btree_opf,
- typentry->btree_opintype,
- typentry->btree_opintype,
- BTEqualStrategyNumber);
- if (typentry->eq_opr == InvalidOid &&
+ eq_opr = get_opfamily_member(typentry->btree_opf,
+ typentry->btree_opintype,
+ typentry->btree_opintype,
+ BTEqualStrategyNumber);
+ if (eq_opr == InvalidOid &&
typentry->hash_opf != InvalidOid)
- typentry->eq_opr = get_opfamily_member(typentry->hash_opf,
- typentry->hash_opintype,
- typentry->hash_opintype,
- HTEqualStrategyNumber);
+ eq_opr = get_opfamily_member(typentry->hash_opf,
+ typentry->hash_opintype,
+ typentry->hash_opintype,
+ HTEqualStrategyNumber);
+
+ /*
+ * If the proposed equality operator is array_eq or record_eq,
+ * check to see if the element type or column types support equality.
+ * If not, array_eq or record_eq would fail at runtime, so we don't
+ * want to report that the type has equality.
+ */
+ if (eq_opr == ARRAY_EQ_OP &&
+ !array_element_has_equality(typentry))
+ eq_opr = InvalidOid;
+ else if (eq_opr == RECORD_EQ_OP &&
+ !record_fields_have_equality(typentry))
+ eq_opr = InvalidOid;
+
+ typentry->eq_opr = eq_opr;
/*
* Reset info about hash function whenever we pick up new info about
@@ -279,32 +315,70 @@ lookup_type_cache(Oid type_id, int flags)
}
if ((flags & TYPECACHE_LT_OPR) && typentry->lt_opr == InvalidOid)
{
+ Oid lt_opr = InvalidOid;
+
if (typentry->btree_opf != InvalidOid)
- typentry->lt_opr = get_opfamily_member(typentry->btree_opf,
- typentry->btree_opintype,
- typentry->btree_opintype,
- BTLessStrategyNumber);
+ lt_opr = get_opfamily_member(typentry->btree_opf,
+ typentry->btree_opintype,
+ typentry->btree_opintype,
+ BTLessStrategyNumber);
+
+ /* As above, make sure array_cmp or record_cmp will succeed */
+ if (lt_opr == ARRAY_LT_OP &&
+ !array_element_has_compare(typentry))
+ lt_opr = InvalidOid;
+ else if (lt_opr == RECORD_LT_OP &&
+ !record_fields_have_compare(typentry))
+ lt_opr = InvalidOid;
+
+ typentry->lt_opr = lt_opr;
}
if ((flags & TYPECACHE_GT_OPR) && typentry->gt_opr == InvalidOid)
{
+ Oid gt_opr = InvalidOid;
+
if (typentry->btree_opf != InvalidOid)
- typentry->gt_opr = get_opfamily_member(typentry->btree_opf,
- typentry->btree_opintype,
- typentry->btree_opintype,
- BTGreaterStrategyNumber);
+ gt_opr = get_opfamily_member(typentry->btree_opf,
+ typentry->btree_opintype,
+ typentry->btree_opintype,
+ BTGreaterStrategyNumber);
+
+ /* As above, make sure array_cmp or record_cmp will succeed */
+ if (gt_opr == ARRAY_GT_OP &&
+ !array_element_has_compare(typentry))
+ gt_opr = InvalidOid;
+ else if (gt_opr == RECORD_GT_OP &&
+ !record_fields_have_compare(typentry))
+ gt_opr = InvalidOid;
+
+ typentry->gt_opr = gt_opr;
}
if ((flags & (TYPECACHE_CMP_PROC | TYPECACHE_CMP_PROC_FINFO)) &&
typentry->cmp_proc == InvalidOid)
{
+ Oid cmp_proc = InvalidOid;
+
if (typentry->btree_opf != InvalidOid)
- typentry->cmp_proc = get_opfamily_proc(typentry->btree_opf,
- typentry->btree_opintype,
- typentry->btree_opintype,
- BTORDER_PROC);
+ cmp_proc = get_opfamily_proc(typentry->btree_opf,
+ typentry->btree_opintype,
+ typentry->btree_opintype,
+ BTORDER_PROC);
+
+ /* As above, make sure array_cmp or record_cmp will succeed */
+ if (cmp_proc == F_BTARRAYCMP &&
+ !array_element_has_compare(typentry))
+ cmp_proc = InvalidOid;
+ else if (cmp_proc == F_BTRECORDCMP &&
+ !record_fields_have_compare(typentry))
+ cmp_proc = InvalidOid;
+
+ typentry->cmp_proc = cmp_proc;
}
if ((flags & (TYPECACHE_HASH_PROC | TYPECACHE_HASH_PROC_FINFO)) &&
typentry->hash_proc == InvalidOid)
{
+ Oid hash_proc = InvalidOid;
+
/*
* We insist that the eq_opr, if one has been determined, match the
* hash opclass; else report there is no hash function.
@@ -315,10 +389,21 @@ lookup_type_cache(Oid type_id, int flags)
typentry->hash_opintype,
typentry->hash_opintype,
HTEqualStrategyNumber)))
- typentry->hash_proc = get_opfamily_proc(typentry->hash_opf,
- typentry->hash_opintype,
- typentry->hash_opintype,
- HASHPROC);
+ hash_proc = get_opfamily_proc(typentry->hash_opf,
+ typentry->hash_opintype,
+ typentry->hash_opintype,
+ HASHPROC);
+
+ /*
+ * As above, make sure hash_array will succeed. We don't currently
+ * support hashing for composite types, but when we do, we'll need
+ * more logic here to check that case too.
+ */
+ if (hash_proc == F_HASH_ARRAY &&
+ !array_element_has_hashing(typentry))
+ hash_proc = InvalidOid;
+
+ typentry->hash_proc = hash_proc;
}
/*
@@ -361,31 +446,166 @@ lookup_type_cache(Oid type_id, int flags)
typentry->tupDesc == NULL &&
typentry->typtype == TYPTYPE_COMPOSITE)
{
- Relation rel;
+ load_typcache_tupdesc(typentry);
+ }
- if (!OidIsValid(typentry->typrelid)) /* should not happen */
- elog(ERROR, "invalid typrelid for composite type %u",
- typentry->type_id);
- rel = relation_open(typentry->typrelid, AccessShareLock);
- Assert(rel->rd_rel->reltype == typentry->type_id);
+ return typentry;
+}
- /*
- * Link to the tupdesc and increment its refcount (we assert it's a
- * refcounted descriptor). We don't use IncrTupleDescRefCount() for
- * this, because the reference mustn't be entered in the current
- * resource owner; it can outlive the current query.
- */
- typentry->tupDesc = RelationGetDescr(rel);
+/*
+ * load_typcache_tupdesc --- helper routine to set up composite type's tupDesc
+ */
+static void
+load_typcache_tupdesc(TypeCacheEntry *typentry)
+{
+ Relation rel;
+
+ if (!OidIsValid(typentry->typrelid)) /* should not happen */
+ elog(ERROR, "invalid typrelid for composite type %u",
+ typentry->type_id);
+ rel = relation_open(typentry->typrelid, AccessShareLock);
+ Assert(rel->rd_rel->reltype == typentry->type_id);
+
+ /*
+ * Link to the tupdesc and increment its refcount (we assert it's a
+ * refcounted descriptor). We don't use IncrTupleDescRefCount() for
+ * this, because the reference mustn't be entered in the current
+ * resource owner; it can outlive the current query.
+ */
+ typentry->tupDesc = RelationGetDescr(rel);
+
+ Assert(typentry->tupDesc->tdrefcount > 0);
+ typentry->tupDesc->tdrefcount++;
+
+ relation_close(rel, AccessShareLock);
+}
+
+
+/*
+ * array_element_has_equality and friends are helper routines to check
+ * whether we should believe that array_eq and related functions will work
+ * on the given array type or composite type.
+ *
+ * The logic above may call these repeatedly on the same type entry, so we
+ * make use of the typentry->flags field to cache the results once known.
+ * Also, we assume that we'll probably want all these facts about the type
+ * if we want any, so we cache them all using only one lookup of the
+ * component datatype(s).
+ */
+
+static bool
+array_element_has_equality(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_array_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_EQUALITY) != 0;
+}
- Assert(typentry->tupDesc->tdrefcount > 0);
- typentry->tupDesc->tdrefcount++;
+static bool
+array_element_has_compare(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_array_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_COMPARE) != 0;
+}
+
+static bool
+array_element_has_hashing(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_ELEM_PROPERTIES))
+ cache_array_element_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_ELEM_HASHING) != 0;
+}
- relation_close(rel, AccessShareLock);
+static void
+cache_array_element_properties(TypeCacheEntry *typentry)
+{
+ Oid elem_type = get_base_element_type(typentry->type_id);
+
+ if (OidIsValid(elem_type))
+ {
+ TypeCacheEntry *elementry;
+
+ elementry = lookup_type_cache(elem_type,
+ TYPECACHE_EQ_OPR |
+ TYPECACHE_CMP_PROC |
+ TYPECACHE_HASH_PROC);
+ if (OidIsValid(elementry->eq_opr))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_EQUALITY;
+ if (OidIsValid(elementry->cmp_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_COMPARE;
+ if (OidIsValid(elementry->hash_proc))
+ typentry->flags |= TCFLAGS_HAVE_ELEM_HASHING;
}
+ typentry->flags |= TCFLAGS_CHECKED_ELEM_PROPERTIES;
+}
- return typentry;
+static bool
+record_fields_have_equality(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
+ cache_record_field_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_FIELD_EQUALITY) != 0;
+}
+
+static bool
+record_fields_have_compare(TypeCacheEntry *typentry)
+{
+ if (!(typentry->flags & TCFLAGS_CHECKED_FIELD_PROPERTIES))
+ cache_record_field_properties(typentry);
+ return (typentry->flags & TCFLAGS_HAVE_FIELD_COMPARE) != 0;
+}
+
+static void
+cache_record_field_properties(TypeCacheEntry *typentry)
+{
+ /*
+ * For type RECORD, we can't really tell what will work, since we don't
+ * have access here to the specific anonymous type. Just assume that
+ * everything will (we may get a failure at runtime ...)
+ */
+ if (typentry->type_id == RECORDOID)
+ typentry->flags |= (TCFLAGS_HAVE_FIELD_EQUALITY |
+ TCFLAGS_HAVE_FIELD_COMPARE);
+ else if (typentry->typtype == TYPTYPE_COMPOSITE)
+ {
+ TupleDesc tupdesc;
+ int newflags;
+ int i;
+
+ /* Fetch composite type's tupdesc if we don't have it already */
+ if (typentry->tupDesc == NULL)
+ load_typcache_tupdesc(typentry);
+ tupdesc = typentry->tupDesc;
+
+ /* Have each property if all non-dropped fields have the property */
+ newflags = (TCFLAGS_HAVE_FIELD_EQUALITY |
+ TCFLAGS_HAVE_FIELD_COMPARE);
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ TypeCacheEntry *fieldentry;
+
+ if (tupdesc->attrs[i]->attisdropped)
+ continue;
+
+ fieldentry = lookup_type_cache(tupdesc->attrs[i]->atttypid,
+ TYPECACHE_EQ_OPR |
+ TYPECACHE_CMP_PROC);
+ if (!OidIsValid(fieldentry->eq_opr))
+ newflags &= ~TCFLAGS_HAVE_FIELD_EQUALITY;
+ if (!OidIsValid(fieldentry->cmp_proc))
+ newflags &= ~TCFLAGS_HAVE_FIELD_COMPARE;
+
+ /* We can drop out of the loop once we disprove all bits */
+ if (newflags == 0)
+ break;
+ }
+ typentry->flags |= newflags;
+ }
+ typentry->flags |= TCFLAGS_CHECKED_FIELD_PROPERTIES;
}
+
/*
* lookup_rowtype_tupdesc_internal --- internal routine to lookup a rowtype
*
@@ -585,7 +805,8 @@ assign_record_type_typmod(TupleDesc tupDesc)
* Relcache inval callback function
*
* Delete the cached tuple descriptor (if any) for the given rel's composite
- * type, or for all composite types if relid == InvalidOid.
+ * type, or for all composite types if relid == InvalidOid. Also reset
+ * whatever info we have cached about the composite type's comparability.
*
* This is called when a relcache invalidation event occurs for the given
* relid. We must scan the whole typcache hash since we don't know the
@@ -611,12 +832,15 @@ TypeCacheRelCallback(Datum arg, Oid relid)
hash_seq_init(&status, TypeCacheHash);
while ((typentry = (TypeCacheEntry *) hash_seq_search(&status)) != NULL)
{
- if (typentry->tupDesc == NULL)
- continue; /* not composite, or tupdesc hasn't been
- * requested */
+ if (typentry->typtype != TYPTYPE_COMPOSITE)
+ continue; /* skip non-composites */
- /* Delete if match, or if we're zapping all composite types */
- if (relid == typentry->typrelid || relid == InvalidOid)
+ /* Skip if no match, unless we're zapping all composite types */
+ if (relid != typentry->typrelid && relid != InvalidOid)
+ continue;
+
+ /* Delete tupdesc if we have it */
+ if (typentry->tupDesc != NULL)
{
/*
* Release our refcount, and free the tupdesc if none remain.
@@ -628,6 +852,17 @@ TypeCacheRelCallback(Datum arg, Oid relid)
FreeTupleDesc(typentry->tupDesc);
typentry->tupDesc = NULL;
}
+
+ /* Reset equality/comparison/hashing information */
+ typentry->eq_opr = InvalidOid;
+ typentry->lt_opr = InvalidOid;
+ typentry->gt_opr = InvalidOid;
+ typentry->cmp_proc = InvalidOid;
+ typentry->hash_proc = InvalidOid;
+ typentry->eq_opr_finfo.fn_oid = InvalidOid;
+ typentry->cmp_proc_finfo.fn_oid = InvalidOid;
+ typentry->hash_proc_finfo.fn_oid = InvalidOid;
+ typentry->flags = 0;
}
}
diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h
index c9332777c1b..64f1391b000 100644
--- a/src/include/catalog/pg_operator.h
+++ b/src/include/catalog/pg_operator.h
@@ -1647,12 +1647,15 @@ DESCR("text search match");
/* generic record comparison operators */
DATA(insert OID = 2988 ( "=" PGNSP PGUID b t f 2249 2249 16 2988 2989 record_eq eqsel eqjoinsel ));
DESCR("equal");
+#define RECORD_EQ_OP 2988
DATA(insert OID = 2989 ( "<>" PGNSP PGUID b f f 2249 2249 16 2989 2988 record_ne neqsel neqjoinsel ));
DESCR("not equal");
DATA(insert OID = 2990 ( "<" PGNSP PGUID b f f 2249 2249 16 2991 2993 record_lt scalarltsel scalarltjoinsel ));
DESCR("less than");
+#define RECORD_LT_OP 2990
DATA(insert OID = 2991 ( ">" PGNSP PGUID b f f 2249 2249 16 2990 2992 record_gt scalargtsel scalargtjoinsel ));
DESCR("greater than");
+#define RECORD_GT_OP 2991
DATA(insert OID = 2992 ( "<=" PGNSP PGUID b f f 2249 2249 16 2993 2991 record_le scalarltsel scalarltjoinsel ));
DESCR("less than or equal");
DATA(insert OID = 2993 ( ">=" PGNSP PGUID b f f 2249 2249 16 2992 2990 record_ge scalargtsel scalargtjoinsel ));
diff --git a/src/include/utils/typcache.h b/src/include/utils/typcache.h
index e2f8c9ce3cf..eb93c1d3b54 100644
--- a/src/include/utils/typcache.h
+++ b/src/include/utils/typcache.h
@@ -39,7 +39,9 @@ typedef struct TypeCacheEntry
* Information obtained from opfamily entries
*
* These will be InvalidOid if no match could be found, or if the
- * information hasn't yet been requested.
+ * information hasn't yet been requested. Also note that for array and
+ * composite types, typcache.c checks that the contained types are
+ * comparable or hashable before allowing eq_opr etc to become set.
*/
Oid btree_opf; /* the default btree opclass' family */
Oid btree_opintype; /* the default btree opclass' opcintype */
@@ -55,8 +57,8 @@ typedef struct TypeCacheEntry
* Pre-set-up fmgr call info for the equality operator, the btree
* comparison function, and the hash calculation function. These are kept
* in the type cache to avoid problems with memory leaks in repeated calls
- * to array_eq, array_cmp, hash_array. There is not currently a need to
- * maintain call info for the lt_opr or gt_opr.
+ * to functions such as array_eq, array_cmp, hash_array. There is not
+ * currently a need to maintain call info for the lt_opr or gt_opr.
*/
FmgrInfo eq_opr_finfo;
FmgrInfo cmp_proc_finfo;
@@ -69,6 +71,9 @@ typedef struct TypeCacheEntry
*/
TupleDesc tupDesc;
+ /* Private data, for internal use of typcache.c only */
+ int flags; /* flags about what we've computed */
+
/*
* Private information about an enum type. NULL if not enum or
* information hasn't been requested.
diff --git a/src/test/regress/expected/rowtypes.out b/src/test/regress/expected/rowtypes.out
index e5cd71421c6..9430bd9b486 100644
--- a/src/test/regress/expected/rowtypes.out
+++ b/src/test/regress/expected/rowtypes.out
@@ -286,6 +286,16 @@ select row(1,1.1) = any (array[ row(7,7.7), row(1,1.0), row(0,0.0) ]);
f
(1 row)
+-- Check behavior with a non-comparable rowtype
+create type cantcompare as (p point, r float8);
+create temp table cc (f1 cantcompare);
+insert into cc values('("(1,2)",3)');
+insert into cc values('("(4,5)",6)');
+select * from cc order by f1; -- fail, but should complain about cantcompare
+ERROR: could not identify an ordering operator for type cantcompare
+LINE 1: select * from cc order by f1;
+ ^
+HINT: Use an explicit ordering operator or modify the query.
--
-- Test case derived from bug #5716: check multiple uses of a rowtype result
--
diff --git a/src/test/regress/sql/rowtypes.sql b/src/test/regress/sql/rowtypes.sql
index 9041df147fe..55e1ff9a9e9 100644
--- a/src/test/regress/sql/rowtypes.sql
+++ b/src/test/regress/sql/rowtypes.sql
@@ -118,6 +118,13 @@ select array[ row(1,2), row(3,4), row(5,6) ];
select row(1,1.1) = any (array[ row(7,7.7), row(1,1.1), row(0,0.0) ]);
select row(1,1.1) = any (array[ row(7,7.7), row(1,1.0), row(0,0.0) ]);
+-- Check behavior with a non-comparable rowtype
+create type cantcompare as (p point, r float8);
+create temp table cc (f1 cantcompare);
+insert into cc values('("(1,2)",3)');
+insert into cc values('("(4,5)",6)');
+select * from cc order by f1; -- fail, but should complain about cantcompare
+
--
-- Test case derived from bug #5716: check multiple uses of a rowtype result
--