*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
/*
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
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;
+}
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
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;