From a60f9db508014c705ed2624cffa424d04549fb77 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 5 Jan 2003 00:56:40 +0000 Subject: [PATCH] Fix inherited UPDATE for cases where child column numbering doesn't match parent table. This used to work, but was broken in 7.3 by rearrangement of code that handles targetlist sorting. Add a regression test to catch future breakage. --- src/backend/optimizer/prep/prepunion.c | 112 +++++++++++++++++++++- src/test/regress/expected/alter_table.out | 28 ++++++ src/test/regress/sql/alter_table.sql | 18 ++++ 3 files changed, 155 insertions(+), 3 deletions(-) diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 5e44a64c01..4d5adc4d47 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.83 2002/12/14 00:17:57 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.84 2003/01/05 00:56:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -67,6 +67,7 @@ static List *generate_append_tlist(List *colTypes, bool flag, List *refnames_tlist); static Node *adjust_inherited_attrs_mutator(Node *node, adjust_inherited_attrs_context *context); +static List *adjust_inherited_tlist(List *tlist, Oid new_relid); /* @@ -768,10 +769,17 @@ adjust_inherited_attrs(Node *node, Query *newnode; FLATCOPY(newnode, query, Query); - if (newnode->resultRelation == old_rt_index) - newnode->resultRelation = new_rt_index; query_tree_mutator(newnode, adjust_inherited_attrs_mutator, (void *) &context, QTW_IGNORE_SUBQUERIES); + if (newnode->resultRelation == old_rt_index) + { + newnode->resultRelation = new_rt_index; + /* Fix tlist resnos too, if it's inherited UPDATE */ + if (newnode->commandType == CMD_UPDATE) + newnode->targetList = + adjust_inherited_tlist(newnode->targetList, + new_relid); + } return (Node *) newnode; } else @@ -887,3 +895,101 @@ adjust_inherited_attrs_mutator(Node *node, return expression_tree_mutator(node, adjust_inherited_attrs_mutator, (void *) context); } + +/* + * Adjust the targetlist entries of an inherited UPDATE operation + * + * The expressions have already been fixed, but we have to make sure that + * the target resnos match the child table (they may not, in the case of + * a column that was added after-the-fact by ALTER TABLE). In some cases + * this can force us to re-order the tlist to preserve resno ordering. + * (We do all this work in special cases so that preptlist.c is fast for + * the typical case.) + * + * The given tlist has already been through expression_tree_mutator; + * therefore the TargetEntry nodes are fresh copies that it's okay to + * scribble on. But the Resdom nodes have not been copied; make new ones + * if we need to change them! + * + * Note that this is not needed for INSERT because INSERT isn't inheritable. + */ +static List * +adjust_inherited_tlist(List *tlist, Oid new_relid) +{ + bool changed_it = false; + List *tl; + List *new_tlist; + bool more; + int attrno; + + /* Scan tlist and update resnos to match attnums of new_relid */ + foreach(tl, tlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(tl); + Resdom *resdom = tle->resdom; + + if (resdom->resjunk) + continue; /* ignore junk items */ + + attrno = get_attnum(new_relid, resdom->resname); + if (attrno == InvalidAttrNumber) + elog(ERROR, "Relation \"%s\" has no column \"%s\"", + get_rel_name(new_relid), resdom->resname); + if (resdom->resno != attrno) + { + resdom = (Resdom *) copyObject((Node *) resdom); + resdom->resno = attrno; + tle->resdom = resdom; + changed_it = true; + } + } + + /* + * If we changed anything, re-sort the tlist by resno, and make sure + * resjunk entries have resnos above the last real resno. The sort + * algorithm is a bit stupid, but for such a seldom-taken path, small + * is probably better than fast. + */ + if (!changed_it) + return tlist; + + new_tlist = NIL; + more = true; + for (attrno = 1; more; attrno++) + { + more = false; + foreach(tl, tlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(tl); + Resdom *resdom = tle->resdom; + + if (resdom->resjunk) + continue; /* ignore junk items */ + + if (resdom->resno == attrno) + new_tlist = lappend(new_tlist, tle); + else if (resdom->resno > attrno) + more = true; + } + } + + foreach(tl, tlist) + { + TargetEntry *tle = (TargetEntry *) lfirst(tl); + Resdom *resdom = tle->resdom; + + if (!resdom->resjunk) + continue; /* here, ignore non-junk items */ + + if (resdom->resno != attrno) + { + resdom = (Resdom *) copyObject((Node *) resdom); + resdom->resno = attrno; + tle->resdom = resdom; + } + new_tlist = lappend(new_tlist, tle); + attrno++; + } + + return new_tlist; +} diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index e0e9b8dc51..877d8bdd84 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -1179,3 +1179,31 @@ order by relname, attnum; drop table p1, p2 cascade; NOTICE: Drop cascades to table c1 NOTICE: Drop cascades to table gc1 +-- test renumbering of child-table columns in inherited operations +create table p1 (f1 int); +create table c1 (f2 text, f3 int) inherits (p1); +alter table p1 add column a1 int check (a1 > 0); +alter table p1 add column f2 text; +NOTICE: ALTER TABLE: merging definition of column "f2" for child c1 +insert into p1 values (1,2,'abc'); +insert into c1 values(11,'xyz',33,0); -- should fail +ERROR: ExecInsert: rejected due to CHECK constraint "p1_a1" on "c1" +insert into c1 values(11,'xyz',33,22); +select * from p1; + f1 | a1 | f2 +----+----+----- + 1 | 2 | abc + 11 | 22 | xyz +(2 rows) + +update p1 set a1 = a1 + 1, f2 = upper(f2); +select * from p1; + f1 | a1 | f2 +----+----+----- + 1 | 3 | ABC + 11 | 23 | XYZ +(2 rows) + +drop table p1 cascade; +NOTICE: Drop cascades to table c1 +NOTICE: Drop cascades to constraint p1_a1 on table c1 diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index 4d20705f53..79ea952ade 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -849,3 +849,21 @@ where relname in ('p1','p2','c1','gc1') and attnum > 0 and not attisdropped order by relname, attnum; drop table p1, p2 cascade; + +-- test renumbering of child-table columns in inherited operations + +create table p1 (f1 int); +create table c1 (f2 text, f3 int) inherits (p1); + +alter table p1 add column a1 int check (a1 > 0); +alter table p1 add column f2 text; + +insert into p1 values (1,2,'abc'); +insert into c1 values(11,'xyz',33,0); -- should fail +insert into c1 values(11,'xyz',33,22); + +select * from p1; +update p1 set a1 = a1 + 1, f2 = upper(f2); +select * from p1; + +drop table p1 cascade; -- 2.40.0