Commit a60f9db5 authored by Tom Lane's avatar Tom Lane

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.
parent 17194f41
......@@ -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;
}
......@@ -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
......@@ -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;
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment