Skip to content

Commit a60f9db

Browse files
committed
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.
1 parent 17194f4 commit a60f9db

File tree

3 files changed

+155
-3
lines changed

3 files changed

+155
-3
lines changed

src/backend/optimizer/prep/prepunion.c

Lines changed: 109 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
*
1515
*
1616
* IDENTIFICATION
17-
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.83 2002/12/14 00:17:57 tgl Exp $
17+
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.84 2003/01/05 00:56:40 tgl Exp $
1818
*
1919
*-------------------------------------------------------------------------
2020
*/
@@ -67,6 +67,7 @@ static List *generate_append_tlist(List *colTypes, bool flag,
6767
List *refnames_tlist);
6868
static Node *adjust_inherited_attrs_mutator(Node *node,
6969
adjust_inherited_attrs_context *context);
70+
static List *adjust_inherited_tlist(List *tlist, Oid new_relid);
7071

7172

7273
/*
@@ -768,10 +769,17 @@ adjust_inherited_attrs(Node *node,
768769
Query *newnode;
769770

770771
FLATCOPY(newnode, query, Query);
771-
if (newnode->resultRelation == old_rt_index)
772-
newnode->resultRelation = new_rt_index;
773772
query_tree_mutator(newnode, adjust_inherited_attrs_mutator,
774773
(void *) &context, QTW_IGNORE_SUBQUERIES);
774+
if (newnode->resultRelation == old_rt_index)
775+
{
776+
newnode->resultRelation = new_rt_index;
777+
/* Fix tlist resnos too, if it's inherited UPDATE */
778+
if (newnode->commandType == CMD_UPDATE)
779+
newnode->targetList =
780+
adjust_inherited_tlist(newnode->targetList,
781+
new_relid);
782+
}
775783
return (Node *) newnode;
776784
}
777785
else
@@ -887,3 +895,101 @@ adjust_inherited_attrs_mutator(Node *node,
887895
return expression_tree_mutator(node, adjust_inherited_attrs_mutator,
888896
(void *) context);
889897
}
898+
899+
/*
900+
* Adjust the targetlist entries of an inherited UPDATE operation
901+
*
902+
* The expressions have already been fixed, but we have to make sure that
903+
* the target resnos match the child table (they may not, in the case of
904+
* a column that was added after-the-fact by ALTER TABLE). In some cases
905+
* this can force us to re-order the tlist to preserve resno ordering.
906+
* (We do all this work in special cases so that preptlist.c is fast for
907+
* the typical case.)
908+
*
909+
* The given tlist has already been through expression_tree_mutator;
910+
* therefore the TargetEntry nodes are fresh copies that it's okay to
911+
* scribble on. But the Resdom nodes have not been copied; make new ones
912+
* if we need to change them!
913+
*
914+
* Note that this is not needed for INSERT because INSERT isn't inheritable.
915+
*/
916+
static List *
917+
adjust_inherited_tlist(List *tlist, Oid new_relid)
918+
{
919+
bool changed_it = false;
920+
List *tl;
921+
List *new_tlist;
922+
bool more;
923+
int attrno;
924+
925+
/* Scan tlist and update resnos to match attnums of new_relid */
926+
foreach(tl, tlist)
927+
{
928+
TargetEntry *tle = (TargetEntry *) lfirst(tl);
929+
Resdom *resdom = tle->resdom;
930+
931+
if (resdom->resjunk)
932+
continue; /* ignore junk items */
933+
934+
attrno = get_attnum(new_relid, resdom->resname);
935+
if (attrno == InvalidAttrNumber)
936+
elog(ERROR, "Relation \"%s\" has no column \"%s\"",
937+
get_rel_name(new_relid), resdom->resname);
938+
if (resdom->resno != attrno)
939+
{
940+
resdom = (Resdom *) copyObject((Node *) resdom);
941+
resdom->resno = attrno;
942+
tle->resdom = resdom;
943+
changed_it = true;
944+
}
945+
}
946+
947+
/*
948+
* If we changed anything, re-sort the tlist by resno, and make sure
949+
* resjunk entries have resnos above the last real resno. The sort
950+
* algorithm is a bit stupid, but for such a seldom-taken path, small
951+
* is probably better than fast.
952+
*/
953+
if (!changed_it)
954+
return tlist;
955+
956+
new_tlist = NIL;
957+
more = true;
958+
for (attrno = 1; more; attrno++)
959+
{
960+
more = false;
961+
foreach(tl, tlist)
962+
{
963+
TargetEntry *tle = (TargetEntry *) lfirst(tl);
964+
Resdom *resdom = tle->resdom;
965+
966+
if (resdom->resjunk)
967+
continue; /* ignore junk items */
968+
969+
if (resdom->resno == attrno)
970+
new_tlist = lappend(new_tlist, tle);
971+
else if (resdom->resno > attrno)
972+
more = true;
973+
}
974+
}
975+
976+
foreach(tl, tlist)
977+
{
978+
TargetEntry *tle = (TargetEntry *) lfirst(tl);
979+
Resdom *resdom = tle->resdom;
980+
981+
if (!resdom->resjunk)
982+
continue; /* here, ignore non-junk items */
983+
984+
if (resdom->resno != attrno)
985+
{
986+
resdom = (Resdom *) copyObject((Node *) resdom);
987+
resdom->resno = attrno;
988+
tle->resdom = resdom;
989+
}
990+
new_tlist = lappend(new_tlist, tle);
991+
attrno++;
992+
}
993+
994+
return new_tlist;
995+
}

src/test/regress/expected/alter_table.out

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,3 +1179,31 @@ order by relname, attnum;
11791179
drop table p1, p2 cascade;
11801180
NOTICE: Drop cascades to table c1
11811181
NOTICE: Drop cascades to table gc1
1182+
-- test renumbering of child-table columns in inherited operations
1183+
create table p1 (f1 int);
1184+
create table c1 (f2 text, f3 int) inherits (p1);
1185+
alter table p1 add column a1 int check (a1 > 0);
1186+
alter table p1 add column f2 text;
1187+
NOTICE: ALTER TABLE: merging definition of column "f2" for child c1
1188+
insert into p1 values (1,2,'abc');
1189+
insert into c1 values(11,'xyz',33,0); -- should fail
1190+
ERROR: ExecInsert: rejected due to CHECK constraint "p1_a1" on "c1"
1191+
insert into c1 values(11,'xyz',33,22);
1192+
select * from p1;
1193+
f1 | a1 | f2
1194+
----+----+-----
1195+
1 | 2 | abc
1196+
11 | 22 | xyz
1197+
(2 rows)
1198+
1199+
update p1 set a1 = a1 + 1, f2 = upper(f2);
1200+
select * from p1;
1201+
f1 | a1 | f2
1202+
----+----+-----
1203+
1 | 3 | ABC
1204+
11 | 23 | XYZ
1205+
(2 rows)
1206+
1207+
drop table p1 cascade;
1208+
NOTICE: Drop cascades to table c1
1209+
NOTICE: Drop cascades to constraint p1_a1 on table c1

src/test/regress/sql/alter_table.sql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -849,3 +849,21 @@ where relname in ('p1','p2','c1','gc1') and attnum > 0 and not attisdropped
849849
order by relname, attnum;
850850

851851
drop table p1, p2 cascade;
852+
853+
-- test renumbering of child-table columns in inherited operations
854+
855+
create table p1 (f1 int);
856+
create table c1 (f2 text, f3 int) inherits (p1);
857+
858+
alter table p1 add column a1 int check (a1 > 0);
859+
alter table p1 add column f2 text;
860+
861+
insert into p1 values (1,2,'abc');
862+
insert into c1 values(11,'xyz',33,0); -- should fail
863+
insert into c1 values(11,'xyz',33,22);
864+
865+
select * from p1;
866+
update p1 set a1 = a1 + 1, f2 = upper(f2);
867+
select * from p1;
868+
869+
drop table p1 cascade;

0 commit comments

Comments
 (0)