diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 49a6f73..d43efc3 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -99,7 +99,6 @@ typedef struct OnCommitItem static List *on_commits = NIL; - /* * State information for ALTER TABLE * @@ -132,6 +131,7 @@ typedef struct AlteredTableInfo Oid relid; /* Relation to work on */ char relkind; /* Its relkind */ TupleDesc oldDesc; /* Pre-modification tuple descriptor */ + bool relVisited; /* Was the rel visited before, for the current subcommand */ /* Information saved by Phase 1 for Phase 2: */ List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */ /* Information saved by Phases 1/2 for Phase 3: */ @@ -335,6 +335,7 @@ static void ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode) static void copy_relation_data(SMgrRelation rel, SMgrRelation dst, ForkNumber forkNum, bool istemp); static const char *storage_name(char c); +static void ATResetRelVisited(List **wqueue); /* ---------------------------------------------------------------- @@ -2583,6 +2584,7 @@ ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode) { AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd); + ATResetRelVisited(&wqueue); ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode); } @@ -3503,6 +3505,23 @@ ATGetQueueEntry(List **wqueue, Relation rel) } /* + * ATResetRelVisited: reset the visitation info for each rel in the working + * queue, so it can be used for the next subcommand. + */ +static void +ATResetRelVisited(List **wqueue) +{ + AlteredTableInfo *tab; + ListCell *ltab; + + foreach(ltab, *wqueue) + { + tab = (AlteredTableInfo *) lfirst(ltab); + tab->relVisited = false; + } +} + +/* * ATSimplePermissions * * - Ensure that it is a relation (or possibly a view) @@ -3618,19 +3637,29 @@ ATSimpleRecursion(List **wqueue, Relation rel, /* * ATOneLevelRecursion * - * Here, we visit only direct inheritance children. It is expected that - * the command's prep routine will recurse again to find indirect children. - * When using this technique, a multiply-inheriting child will be visited - * multiple times. + * Here, we visit only direct inheritance children. It is expected that the + * command's prep routine will recurse again to find indirect children. When + * using this technique, a multiple-inheriting child will be visited multiple + * times. Childs of multiple-inheriting childs however are only visited once + * for each parent. */ static void ATOneLevelRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode) { Oid relid = RelationGetRelid(rel); + AlteredTableInfo *tab = ATGetQueueEntry(wqueue, rel); ListCell *child; List *children; + /* If we already visited the current multiple-inheriting relation, we + * mustn't recurse to it's child tables, because they've already been + * visited. Visiting them would lead to an incorrect value for + * attinhcount. */ + if (tab->relVisited) + return; + tab->relVisited = true; + children = find_inheritance_children(relid, lockmode); foreach(child, children) @@ -4891,6 +4920,15 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, CommandCounterIncrement(); /* + * If the constraint got merged with an existing constraint, we're done. + * We mustn't recurse to child tables in this case, because they've already + * got the constraint, and visiting them again would lead to an incorrect + * value for coninhcount. + */ + if (newcons == NIL) + return; + + /* * Propagate to children as appropriate. Unlike most other ALTER * routines, we have to do this one level of recursion at a time; we can't * use find_all_inheritors to do it in one pass.