diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index bc80caee117..47379fef220 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -414,11 +414,6 @@ DecrTupleDescRefCount(TupleDesc tupdesc) /* * Compare two TupleDesc structures for logical equality - * - * Note: we deliberately do not check the attrelid and tdtypmod fields. - * This allows typcache.c to use this routine to see if a cached record type - * matches a requested type, and is harmless for relcache.c's uses. - * We don't compare tdrefcount, either. */ bool equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) @@ -431,6 +426,8 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) if (tupdesc1->tdtypeid != tupdesc2->tdtypeid) return false; + /* tdtypmod and tdrefcount are not checked */ + for (i = 0; i < tupdesc1->natts; i++) { Form_pg_attribute attr1 = TupleDescAttr(tupdesc1, i); @@ -561,17 +558,68 @@ equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2) } /* - * hashTupleDesc - * Compute a hash value for a tuple descriptor. + * equalRowTypes * - * If two tuple descriptors would be considered equal by equalTupleDescs() + * This determines whether two tuple descriptors have equal row types. This + * only checks those fields in pg_attribute that are applicable for row types, + * while ignoring those fields that define the physical row storage or those + * that define table column metadata. + * + * Specifically, this checks: + * + * - same number of attributes + * - same composite type ID (but could both be zero) + * - corresponding attributes (in order) have same the name, type, typmod, + * collation + * + * This is used to check whether two record types are compatible, whether + * function return row types are the same, and other similar situations. + * + * (XXX There was some discussion whether attndims should be checked here, but + * for now it has been decided not to.) + * + * Note: We deliberately do not check the tdtypmod field. This allows + * typcache.c to use this routine to see if a cached record type matches a + * requested type. + */ +bool +equalRowTypes(TupleDesc tupdesc1, TupleDesc tupdesc2) +{ + if (tupdesc1->natts != tupdesc2->natts) + return false; + if (tupdesc1->tdtypeid != tupdesc2->tdtypeid) + return false; + + for (int i = 0; i < tupdesc1->natts; i++) + { + Form_pg_attribute attr1 = TupleDescAttr(tupdesc1, i); + Form_pg_attribute attr2 = TupleDescAttr(tupdesc2, i); + + if (strcmp(NameStr(attr1->attname), NameStr(attr2->attname)) != 0) + return false; + if (attr1->atttypid != attr2->atttypid) + return false; + if (attr1->atttypmod != attr2->atttypmod) + return false; + if (attr1->attcollation != attr2->attcollation) + return false; + + /* Record types derived from tables could have dropped fields. */ + if (attr1->attisdropped != attr2->attisdropped) + return false; + } + + return true; +} + +/* + * hashRowType + * + * If two tuple descriptors would be considered equal by equalRowTypes() * then their hash value will be equal according to this function. - * - * Note that currently contents of constraint are not hashed - it'd be a bit - * painful to do so, and conflicts just due to constraints are unlikely. */ uint32 -hashTupleDesc(TupleDesc desc) +hashRowType(TupleDesc desc) { uint32 s; int i; diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index e6c6001f399..528c17cd7f6 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -437,7 +437,7 @@ ProcedureCreate(const char *procedureName, if (olddesc == NULL && newdesc == NULL) /* ok, both are runtime-defined RECORDs */ ; else if (olddesc == NULL || newdesc == NULL || - !equalTupleDescs(olddesc, newdesc)) + !equalRowTypes(olddesc, newdesc)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change return type of existing function"), diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 28880bd2991..8a82af4a4ca 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -1581,8 +1581,8 @@ acquire_inherited_sample_rows(Relation onerel, int elevel, /* We may need to convert from child's rowtype to parent's */ if (childrows > 0 && - !equalTupleDescs(RelationGetDescr(childrel), - RelationGetDescr(onerel))) + !equalRowTypes(RelationGetDescr(childrel), + RelationGetDescr(onerel))) { TupleConversionMap *map; diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index becc1fb4583..fdad8338324 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -30,7 +30,7 @@ #include "utils/lsyscache.h" #include "utils/rel.h" -static void checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc); +static void checkViewColumns(TupleDesc newdesc, TupleDesc olddesc); /*--------------------------------------------------------------------- * DefineVirtualRelation @@ -130,7 +130,7 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace, * column list. */ descriptor = BuildDescForRelation(attrList); - checkViewTupleDesc(descriptor, rel->rd_att); + checkViewColumns(descriptor, rel->rd_att); /* * If new attributes have been added, we must add pg_attribute entries @@ -258,13 +258,13 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace, } /* - * Verify that tupledesc associated with proposed new view definition - * matches tupledesc of old view. This is basically a cut-down version - * of equalTupleDescs(), with code added to generate specific complaints. - * Also, we allow the new tupledesc to have more columns than the old. + * Verify that the columns associated with proposed new view definition match + * the columns of the old view. This is similar to equalRowTypes(), with code + * added to generate specific complaints. Also, we allow the new view to have + * more columns than the old. */ static void -checkViewTupleDesc(TupleDesc newdesc, TupleDesc olddesc) +checkViewColumns(TupleDesc newdesc, TupleDesc olddesc) { int i; diff --git a/src/backend/utils/cache/plancache.c b/src/backend/utils/cache/plancache.c index e16f4c36ec5..5af1a168ec2 100644 --- a/src/backend/utils/cache/plancache.c +++ b/src/backend/utils/cache/plancache.c @@ -727,8 +727,7 @@ RevalidateCachedQuery(CachedPlanSource *plansource, PopActiveSnapshot(); /* - * Check or update the result tupdesc. XXX should we use a weaker - * condition than equalTupleDescs() here? + * Check or update the result tupdesc. * * We assume the parameter types didn't change from the first time, so no * need to update that. @@ -739,7 +738,7 @@ RevalidateCachedQuery(CachedPlanSource *plansource, /* OK, doesn't return tuples */ } else if (resultDesc == NULL || plansource->resultDesc == NULL || - !equalTupleDescs(resultDesc, plansource->resultDesc)) + !equalRowTypes(resultDesc, plansource->resultDesc)) { /* can we give a better error message? */ if (plansource->fixed_result) diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c index 0d4d0b0a154..d86c3b06fa0 100644 --- a/src/backend/utils/cache/typcache.c +++ b/src/backend/utils/cache/typcache.c @@ -147,7 +147,7 @@ typedef struct TypeCacheEnumData * We use a separate table for storing the definitions of non-anonymous * record types. Once defined, a record type will be remembered for the * life of the backend. Subsequent uses of the "same" record type (where - * sameness means equalTupleDescs) will refer to the existing table entry. + * sameness means equalRowTypes) will refer to the existing table entry. * * Stored record types are remembered in a linear array of TupleDescs, * which can be indexed quickly with the assigned typmod. There is also @@ -231,7 +231,7 @@ shared_record_table_compare(const void *a, const void *b, size_t size, else t2 = k2->u.local_tupdesc; - return equalTupleDescs(t1, t2) ? 0 : 1; + return equalRowTypes(t1, t2) ? 0 : 1; } /* @@ -249,7 +249,7 @@ shared_record_table_hash(const void *a, size_t size, void *arg) else t = k->u.local_tupdesc; - return hashTupleDesc(t); + return hashRowType(t); } /* Parameters for SharedRecordTypmodRegistry's TupleDesc table. */ @@ -1927,7 +1927,7 @@ record_type_typmod_hash(const void *data, size_t size) { RecordCacheEntry *entry = (RecordCacheEntry *) data; - return hashTupleDesc(entry->tupdesc); + return hashRowType(entry->tupdesc); } /* @@ -1939,7 +1939,7 @@ record_type_typmod_compare(const void *a, const void *b, size_t size) RecordCacheEntry *left = (RecordCacheEntry *) a; RecordCacheEntry *right = (RecordCacheEntry *) b; - return equalTupleDescs(left->tupdesc, right->tupdesc) ? 0 : 1; + return equalRowTypes(left->tupdesc, right->tupdesc) ? 0 : 1; } /* diff --git a/src/include/access/tupdesc.h b/src/include/access/tupdesc.h index 3d767dde65d..8930a28d660 100644 --- a/src/include/access/tupdesc.h +++ b/src/include/access/tupdesc.h @@ -126,8 +126,8 @@ extern void DecrTupleDescRefCount(TupleDesc tupdesc); } while (0) extern bool equalTupleDescs(TupleDesc tupdesc1, TupleDesc tupdesc2); - -extern uint32 hashTupleDesc(TupleDesc desc); +extern bool equalRowTypes(TupleDesc tupdesc1, TupleDesc tupdesc2); +extern uint32 hashRowType(TupleDesc desc); extern void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber,