Fix inherited UPDATE for cases where child column numbering doesn't
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 5 Jan 2003 00:56:40 +0000 (00:56 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 5 Jan 2003 00:56:40 +0000 (00:56 +0000)
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
src/test/regress/expected/alter_table.out
src/test/regress/sql/alter_table.sql

index 5e44a64c01246a0715ebc5a4eda4723150def204..4d5adc4d47bc45334041c8cbacb907486f3d544e 100644 (file)
@@ -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;
+}
index e0e9b8dc5156dea34d7826c5eaf1de3e783d230b..877d8bdd84bdb1aa27789ca268a35aa5082274f1 100644 (file)
@@ -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
index 4d20705f53a5a4b8bfdf46a55c3ba19d57d138a2..79ea952adeefb2f15e1e52181756abaa6a0ca9f9 100644 (file)
@@ -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;