diff --git a/src/backend/access/common/tupconvert.c b/src/backend/access/common/tupconvert.c index 2d0d2f4b32..578bf7f2ba 100644 --- a/src/backend/access/common/tupconvert.c +++ b/src/backend/access/common/tupconvert.c @@ -297,6 +297,7 @@ convert_tuples_by_name_map(TupleDesc indesc, AttrNumber *attrMap; int n; int i; + int outnondropped = 0; n = outdesc->natts; attrMap = (AttrNumber *) palloc0(n * sizeof(AttrNumber)); @@ -313,9 +314,22 @@ convert_tuples_by_name_map(TupleDesc indesc, attname = NameStr(outatt->attname); atttypid = outatt->atttypid; atttypmod = outatt->atttypmod; + + /* + * Now search for an attribute with the same name in the indesc. It + * seems likely that a partitioned table will have the attributes in + * the same order as the partition, so we optimistically start our + * search at the same attnum (minus the so-far counted number of + * dropped columns in outdesc). If a column were dropped in outdesc + * and not indesc then if we didn't account for the dropped column, it + * could mean we end up starting our search one column after the one + * we want to find, resulting in only finding our target on the final + * column we check. + */ for (j = 0; j < indesc->natts; j++) { - Form_pg_attribute inatt = TupleDescAttr(indesc, j); + int k = (outnondropped + j) % indesc->natts; + Form_pg_attribute inatt = TupleDescAttr(indesc, k); if (inatt->attisdropped) continue; @@ -330,7 +344,7 @@ convert_tuples_by_name_map(TupleDesc indesc, attname, format_type_be(outdesc->tdtypeid), format_type_be(indesc->tdtypeid)))); - attrMap[i] = (AttrNumber) (j + 1); + attrMap[i] = inatt->attnum; break; } } @@ -342,6 +356,9 @@ convert_tuples_by_name_map(TupleDesc indesc, attname, format_type_be(outdesc->tdtypeid), format_type_be(indesc->tdtypeid)))); + + /* keep track of number of non-dropped outdesc columns */ + outnondropped++; } return attrMap; diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 2d470240d5..5a1e7a12ec 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -51,6 +51,7 @@ #include "utils/lsyscache.h" #include "utils/rel.h" #include "utils/selfuncs.h" +#include "utils/syscache.h" typedef struct @@ -1896,6 +1897,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation, List *vars = NIL; TupleDesc old_tupdesc = RelationGetDescr(oldrelation); TupleDesc new_tupdesc = RelationGetDescr(newrelation); + Oid new_relid = RelationGetRelid(newrelation); int oldnatts = old_tupdesc->natts; int newnatts = new_tupdesc->natts; int old_attno; @@ -1941,7 +1943,7 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation, * There's no guarantee it'll have the same column position, because * of cases like ALTER TABLE ADD COLUMN and multiple inheritance. * However, in simple cases it will be the same column number, so try - * that before we go groveling through all the columns. + * that before looking for it in syscache. * * Note: the test for (att = ...) != NULL cannot fail, it's just a * notational device to include the assignment into the if-clause. @@ -1953,16 +1955,16 @@ make_inh_translation_list(Relation oldrelation, Relation newrelation, new_attno = old_attno; else { - for (new_attno = 0; new_attno < newnatts; new_attno++) - { - att = TupleDescAttr(new_tupdesc, new_attno); - if (!att->attisdropped && - strcmp(attname, NameStr(att->attname)) == 0) - break; - } - if (new_attno >= newnatts) + HeapTuple newtup = SearchSysCacheAttName(new_relid, attname); + + if (!newtup) elog(ERROR, "could not find inherited attribute \"%s\" of relation \"%s\"", attname, RelationGetRelationName(newrelation)); + + new_attno = ((Form_pg_attribute) GETSTRUCT(newtup))->attnum - 1; + ReleaseSysCache(newtup); + + att = TupleDescAttr(new_tupdesc, new_attno); } /* Found it, check type and collation match */