diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 5e44a64c01246a0715ebc5a4eda4723150def204..4d5adc4d47bc45334041c8cbacb907486f3d544e 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 e0e9b8dc5156dea34d7826c5eaf1de3e783d230b..877d8bdd84bdb1aa27789ca268a35aa5082274f1 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 4d20705f53a5a4b8bfdf46a55c3ba19d57d138a2..79ea952adeefb2f15e1e52181756abaa6a0ca9f9 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;