diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index e70d752..75fd45a 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -7999,12 +7999,96 @@ ATPrepAlterColumnType(List **wqueue, ReleaseSysCache(tuple); /* - * The recursion case is handled by ATSimpleRecursion. However, if we are - * told not to recurse, there had better not be any child tables; else the - * alter would put them out of step. + * Recurse manually, if necessary. We cannot apply ATSimpleRecursion here + * because we need to remap attribute numbers for each child. + * + * If we are told not to recurse, there had better not be any child + * tables; else the alter would put them out of step. */ if (recurse) - ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode); + { + Oid relid = RelationGetRelid(rel); + ListCell *child; + List *children; + + children = find_all_inheritors(relid, lockmode, NULL); + + /* + * find_all_inheritors does the recursive search of the inheritance + * hierarchy, so all we have to do is process all of the relids in the + * list that it returns. + */ + foreach(child, children) + { + Oid childrelid = lfirst_oid(child); + Relation childrel; + AttrNumber *attmap; + AttrNumber parent_attno; + bool found_whole_row; + TupleDesc parentDesc; + TupleDesc childDesc; + + if (childrelid == relid) + continue; + + /* find_all_inheritors already got lock */ + childrel = relation_open(childrelid, NoLock); + CheckTableNotInUse(childrel, "ALTER TABLE"); + + /* + * Build an attribute map for map_variable_attnos. This is O(N^2) + * on the number of attributes ... + */ + parentDesc = RelationGetDescr(rel); + childDesc = RelationGetDescr(childrel); + attmap = (AttrNumber *) palloc0(sizeof(AttrNumber) * + parentDesc->natts); + for (parent_attno = 1; + parent_attno <= parentDesc->natts; + parent_attno++) + { + bool found = false; + AttrNumber child_attno; + + for (child_attno = 1; + child_attno <= childDesc->natts; + child_attno++) + { + if (strncmp(NameStr(parentDesc->attrs[parent_attno - 1]->attname), + NameStr(childDesc->attrs[child_attno - 1]->attname), + NAMEDATALEN) == 0) + { + attmap[parent_attno - 1] = child_attno; + found = true; + break; + } + } + + /* should not happen */ + if (!found) + elog(ERROR, "column \"%s\" not found in child table \"%s\"", + NameStr(parentDesc->attrs[parent_attno - 1]->attname), + RelationGetRelationName(childrel)); + } + + /* + * Queue a command for this child, with remapped attnums. Note + * that ATPrepCmd creates a copy, so there's no need to do that + * here. XXX what about the entry for the parent table? + */ + def->cooked_default = + map_variable_attnos(transform, + 1, 0, + attmap, parentDesc->natts, + &found_whole_row); + if (found_whole_row) + elog(ERROR, "unexpected whole-row reference"); + ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode); + relation_close(childrel, NoLock); + + pfree(attmap); + } + } else if (!recursing && find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL) ereport(ERROR,