diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 324674a5834d18d23c71a4f17d0ace9ca16a2ad6..2f8d0d593e7d1a8eab0f17aa54af4a155792308e 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.51 2002/04/02 08:51:51 inoue Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.52 2002/04/05 05:47:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,19 +27,10 @@
 #include "nodes/makefuncs.h"
 #include "optimizer/prep.h"
 #include "parser/parsetree.h"
-#include "parser/parse_coerce.h"
-#include "parser/parse_expr.h"
-#include "parser/parse_target.h"
-#include "utils/builtins.h"
-#include "utils/lsyscache.h"
 
 
 static List *expand_targetlist(List *tlist, int command_type,
 				  Index result_relation, List *range_table);
-static TargetEntry *process_matched_tle(TargetEntry *src_tle,
-					TargetEntry *prior_tle,
-					int attrno);
-static Node *build_column_default(Relation rel, int attrno);
 
 
 /*
@@ -119,31 +110,25 @@ preprocess_targetlist(List *tlist,
 /*
  * expand_targetlist
  *	  Given a target list as generated by the parser and a result relation,
- *	  add targetlist entries for any missing attributes, and order the
- *	  non-junk attributes in proper field order.
+ *	  add targetlist entries for any missing attributes, and ensure the
+ *	  non-junk attributes appear in proper field order.
+ *
+ * NOTE: if you are tempted to put more processing here, consider whether
+ * it shouldn't go in the rewriter's rewriteTargetList() instead.
  */
 static List *
 expand_targetlist(List *tlist, int command_type,
 				  Index result_relation, List *range_table)
 {
-	int			old_tlist_len = length(tlist);
 	List	   *new_tlist = NIL;
-	bool	   *tlistentry_used;
 	Relation	rel;
 	int			attrno,
-				numattrs,
-				old_tlist_index;
-	List	   *temp;
+				numattrs;
 
 	/*
-	 * Keep a map of which tlist items we have transferred to new list.
+	 * The rewriter should have already ensured that the TLEs are in
+	 * correct order; but we have to insert TLEs for any missing attributes.
 	 *
-	 * +1 here just keeps palloc from complaining if old_tlist_len==0.
-	 */
-	tlistentry_used = (bool *) palloc((old_tlist_len + 1) * sizeof(bool));
-	memset(tlistentry_used, 0, (old_tlist_len + 1) * sizeof(bool));
-
-	/*
 	 * Scan the tuple description in the relation's relcache entry to make
 	 * sure we have all the user attributes in the right order.
 	 */
@@ -154,28 +139,20 @@ expand_targetlist(List *tlist, int command_type,
 	for (attrno = 1; attrno <= numattrs; attrno++)
 	{
 		Form_pg_attribute att_tup = rel->rd_att->attrs[attrno - 1];
-		char	   *attrname = NameStr(att_tup->attname);
 		TargetEntry *new_tle = NULL;
 
-		/*
-		 * We match targetlist entries to attributes using the resname.
-		 * Junk attributes are not candidates to be matched.
-		 */
-		old_tlist_index = 0;
-		foreach(temp, tlist)
+		if (tlist != NIL)
 		{
-			TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
+			TargetEntry *old_tle = (TargetEntry *) lfirst(tlist);
 			Resdom	   *resdom = old_tle->resdom;
 
-			if (!tlistentry_used[old_tlist_index] &&
-				!resdom->resjunk &&
-				strcmp(resdom->resname, attrname) == 0)
+			if (!resdom->resjunk && resdom->resno == attrno)
 			{
-				new_tle = process_matched_tle(old_tle, new_tle, attrno);
-				tlistentry_used[old_tlist_index] = true;
-				/* keep scanning to detect multiple assignments to attr */
+				Assert(strcmp(resdom->resname,
+							  NameStr(att_tup->attname)) == 0);
+				new_tle = old_tle;
+				tlist = lnext(tlist);
 			}
-			old_tlist_index++;
 		}
 
 		if (new_tle == NULL)
@@ -183,7 +160,8 @@ expand_targetlist(List *tlist, int command_type,
 			/*
 			 * Didn't find a matching tlist entry, so make one.
 			 *
-			 * For INSERT, generate an appropriate default value.
+			 * For INSERT, generate a NULL constant.  (We assume the
+			 * rewriter would have inserted any available default value.)
 			 *
 			 * For UPDATE, generate a Var reference to the existing value of
 			 * the attribute, so that it gets copied to the new tuple.
@@ -195,14 +173,20 @@ expand_targetlist(List *tlist, int command_type,
 			switch (command_type)
 			{
 				case CMD_INSERT:
-					new_expr = build_column_default(rel, attrno);
+					new_expr = (Node *) makeConst(atttype,
+												  att_tup->attlen,
+												  (Datum) 0,
+												  true,		/* isnull */
+												  att_tup->attbyval,
+												  false,	/* not a set */
+												  false);
 					break;
 				case CMD_UPDATE:
-						new_expr = (Node *) makeVar(result_relation,
-													attrno,
-													atttype,
-													atttypmod,
-													0);
+					new_expr = (Node *) makeVar(result_relation,
+												attrno,
+												atttype,
+												atttypmod,
+												0);
 					break;
 				default:
 					elog(ERROR, "expand_targetlist: unexpected command_type");
@@ -213,7 +197,7 @@ expand_targetlist(List *tlist, int command_type,
 			new_tle = makeTargetEntry(makeResdom(attrno,
 												 atttype,
 												 atttypmod,
-												 pstrdup(attrname),
+												 pstrdup(NameStr(att_tup->attname)),
 												 false),
 									  new_expr);
 		}
@@ -222,230 +206,32 @@ expand_targetlist(List *tlist, int command_type,
 	}
 
 	/*
-	 * Copy all unprocessed tlist entries to the end of the new tlist,
-	 * making sure they are marked resjunk = true.	Typical junk entries
-	 * include ORDER BY or GROUP BY expressions (are these actually
-	 * possible in an INSERT or UPDATE?), system attribute references,
-	 * etc.
+	 * The remaining tlist entries should be resjunk; append them all to
+	 * the end of the new tlist, making sure they have resnos higher than
+	 * the last real attribute.  (Note: although the rewriter already did
+	 * such renumbering, we have to do it again here in case we are doing
+	 * an UPDATE in an inheritance child table with more columns.)
 	 */
-	old_tlist_index = 0;
-	foreach(temp, tlist)
+	while (tlist)
 	{
-		TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
+		TargetEntry *old_tle = (TargetEntry *) lfirst(tlist);
+		Resdom	   *resdom = old_tle->resdom;
 
-		if (!tlistentry_used[old_tlist_index])
+		if (!resdom->resjunk)
+			elog(ERROR, "expand_targetlist: targetlist is not sorted correctly");
+		/* Get the resno right, but don't copy unnecessarily */
+		if (resdom->resno != attrno)
 		{
-			Resdom	   *resdom = old_tle->resdom;
-
-			if (!resdom->resjunk)
-				elog(ERROR, "Unexpected assignment to attribute \"%s\"",
-					 resdom->resname);
-			/* Get the resno right, but don't copy unnecessarily */
-			if (resdom->resno != attrno)
-			{
-				resdom = (Resdom *) copyObject((Node *) resdom);
-				resdom->resno = attrno;
-				old_tle = makeTargetEntry(resdom, old_tle->expr);
-			}
-			new_tlist = lappend(new_tlist, old_tle);
-			attrno++;
+			resdom = (Resdom *) copyObject((Node *) resdom);
+			resdom->resno = attrno;
+			old_tle = makeTargetEntry(resdom, old_tle->expr);
 		}
-		old_tlist_index++;
+		new_tlist = lappend(new_tlist, old_tle);
+		attrno++;
+		tlist = lnext(tlist);
 	}
 
 	heap_close(rel, AccessShareLock);
 
-	pfree(tlistentry_used);
-
 	return new_tlist;
 }
-
-
-/*
- * Convert a matched TLE from the original tlist into a correct new TLE.
- *
- * This routine checks for multiple assignments to the same target attribute,
- * such as "UPDATE table SET foo = 42, foo = 43".  This is OK only if they
- * are array assignments, ie, "UPDATE table SET foo[2] = 42, foo[4] = 43".
- * If so, we need to merge the operations into a single assignment op.
- * Essentially, the expression we want to produce in this case is like
- *		foo = array_set(array_set(foo, 2, 42), 4, 43)
- */
-static TargetEntry *
-process_matched_tle(TargetEntry *src_tle,
-					TargetEntry *prior_tle,
-					int attrno)
-{
-	Resdom	   *resdom = src_tle->resdom;
-	Node	   *priorbottom;
-	ArrayRef   *newexpr;
-
-	if (prior_tle == NULL)
-	{
-		/*
-		 * Normal case where this is the first assignment to the
-		 * attribute.
-		 *
-		 * We can recycle the old TLE+resdom if right resno; else make a new
-		 * one to avoid modifying the old tlist structure. (Is preserving
-		 * old tlist actually necessary?  Not sure, be safe.)
-		 */
-		if (resdom->resno == attrno)
-			return src_tle;
-		resdom = (Resdom *) copyObject((Node *) resdom);
-		resdom->resno = attrno;
-		return makeTargetEntry(resdom, src_tle->expr);
-	}
-
-	/*
-	 * Multiple assignments to same attribute.	Allow only if all are
-	 * array-assign operators with same bottom array object.
-	 */
-	if (src_tle->expr == NULL || !IsA(src_tle->expr, ArrayRef) ||
-		((ArrayRef *) src_tle->expr)->refassgnexpr == NULL ||
-		prior_tle->expr == NULL || !IsA(prior_tle->expr, ArrayRef) ||
-		((ArrayRef *) prior_tle->expr)->refassgnexpr == NULL ||
-		((ArrayRef *) src_tle->expr)->refelemtype !=
-		((ArrayRef *) prior_tle->expr)->refelemtype)
-		elog(ERROR, "Multiple assignments to same attribute \"%s\"",
-			 resdom->resname);
-
-	/*
-	 * Prior TLE could be a nest of ArrayRefs if we do this more than
-	 * once.
-	 */
-	priorbottom = ((ArrayRef *) prior_tle->expr)->refexpr;
-	while (priorbottom != NULL && IsA(priorbottom, ArrayRef) &&
-		   ((ArrayRef *) priorbottom)->refassgnexpr != NULL)
-		priorbottom = ((ArrayRef *) priorbottom)->refexpr;
-	if (!equal(priorbottom, ((ArrayRef *) src_tle->expr)->refexpr))
-		elog(ERROR, "Multiple assignments to same attribute \"%s\"",
-			 resdom->resname);
-
-	/*
-	 * Looks OK to nest 'em.
-	 */
-	newexpr = makeNode(ArrayRef);
-	memcpy(newexpr, src_tle->expr, sizeof(ArrayRef));
-	newexpr->refexpr = prior_tle->expr;
-
-	resdom = (Resdom *) copyObject((Node *) resdom);
-	resdom->resno = attrno;
-	return makeTargetEntry(resdom, (Node *) newexpr);
-}
-
-
-/*
- * Make an expression tree for the default value for a column.
- *
- * This is used to fill in missing attributes in an INSERT targetlist.
- * We look first to see if the column has a default value expression.
- * If not, generate a constant of the default value for the attribute type,
- * or a NULL if the type has no default value either.
- */
-static Node *
-build_column_default(Relation rel, int attrno)
-{
-	TupleDesc	rd_att = rel->rd_att;
-	Form_pg_attribute att_tup = rd_att->attrs[attrno - 1];
-	Oid			atttype = att_tup->atttypid;
-	int32		atttypmod = att_tup->atttypmod;
-	int16		typlen = att_tup->attlen;
-	bool		typbyval = att_tup->attbyval;
-	Node	   *expr = NULL;
-
-	/*
-	 * Scan to see if relation has a default for this column.
-	 */
-	if (rd_att->constr && rd_att->constr->num_defval > 0)
-	{
-		AttrDefault *defval = rd_att->constr->defval;
-		int			ndef = rd_att->constr->num_defval;
-
-		while (--ndef >= 0)
-		{
-			if (attrno == defval[ndef].adnum)
-			{
-				/*
-				 * Found it, convert string representation to node tree.
-				 */
-				expr = stringToNode(defval[ndef].adbin);
-				break;
-			}
-		}
-	}
-
-	if (expr == NULL)
-	{
-		/*
-		 * No per-column default, so look for a default for the type itself.
-		 */
-		if (att_tup->attisset)
-		{
-			/*
-			 * Set attributes are represented as OIDs no matter what the set
-			 * element type is, and the element type's default is irrelevant
-			 * too.
-			 */
-			typlen = sizeof(Oid);
-			typbyval = true;
-		}
-		else
-		{
-			expr = get_typdefault(atttype);
-		}
-	}
-
-	if (expr == NULL)
-	{
-		/*
-		 * No default anywhere, so generate a NULL constant.
-		 */
-		expr = (Node *) makeConst(atttype,
-								  typlen,
-								  (Datum) 0,
-								  true,			/* isnull */
-								  typbyval,
-								  false,		/* not a set */
-								  false);
-	}
-	else
-	{
-		Oid			exprtype;
-
-		/*
-		 * Make sure the value is coerced to the target column
-		 * type (might not be right type yet if it's not a
-		 * constant!)  This should match the parser's processing of
-		 * non-defaulted expressions --- see
-		 * updateTargetListEntry().
-		 */
-		exprtype = exprType(expr);
-
-		if (exprtype != atttype)
-		{
-			expr = CoerceTargetExpr(NULL, expr, exprtype,
-									atttype, atttypmod);
-
-			/*
-			 * This really shouldn't fail; should have checked the
-			 * default's type when it was created ...
-			 */
-			if (expr == NULL)
-				elog(ERROR, "Column \"%s\" is of type %s"
-					 " but default expression is of type %s"
-					 "\n\tYou will need to rewrite or cast the expression",
-					 NameStr(att_tup->attname),
-					 format_type_be(atttype),
-					 format_type_be(exprtype));
-		}
-
-		/*
-		 * If the column is a fixed-length type, it may need a
-		 * length coercion as well as a type coercion.
-		 */
-		expr = coerce_type_typmod(NULL, expr, atttype, atttypmod);
-	}
-
-	return expr;
-}
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index aebc7b94f86755c128be46b30401190be67cf5da..08d93502497e0ed12c5ee3e0b5170d9932b2ea8a 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.100 2002/03/22 02:56:34 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteHandler.c,v 1.101 2002/04/05 05:47:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,13 +22,15 @@
 #include "optimizer/prep.h"
 #include "optimizer/var.h"
 #include "parser/analyze.h"
+#include "parser/parse_coerce.h"
 #include "parser/parse_expr.h"
 #include "parser/parse_oper.h"
 #include "parser/parse_target.h"
-#include "parser/parsetree.h"
 #include "parser/parse_type.h"
+#include "parser/parsetree.h"
 #include "rewrite/rewriteHandler.h"
 #include "rewrite/rewriteManip.h"
+#include "utils/builtins.h"
 #include "utils/lsyscache.h"
 
 
@@ -38,6 +40,10 @@ static Query *rewriteRuleAction(Query *parsetree,
 				  int rt_index,
 				  CmdType event);
 static List *adjustJoinTreeList(Query *parsetree, bool removert, int rt_index);
+static void rewriteTargetList(Query *parsetree, Relation target_relation);
+static TargetEntry *process_matched_tle(TargetEntry *src_tle,
+										TargetEntry *prior_tle);
+static Node *build_column_default(Relation rel, int attrno);
 static void markQueryForUpdate(Query *qry, bool skipOldNew);
 static List *matchLocks(CmdType event, RuleLock *rulelocks,
 		   int varno, Query *parsetree);
@@ -212,6 +218,289 @@ adjustJoinTreeList(Query *parsetree, bool removert, int rt_index)
 }
 
 
+/*
+ * rewriteTargetList - rewrite INSERT/UPDATE targetlist into standard form
+ *
+ * This has the following responsibilities:
+ *
+ * 1. For an INSERT, add tlist entries to compute default values for any
+ * attributes that have defaults and are not assigned to in the given tlist.
+ * (We do not insert anything for default-less attributes, however.  The
+ * planner will later insert NULLs for them, but there's no reason to slow
+ * down rewriter processing with extra tlist nodes.)
+ *
+ * 2. Merge multiple entries for the same target attribute, or declare error
+ * if we can't.  Presently, multiple entries are only allowed for UPDATE of
+ * an array field, for example "UPDATE table SET foo[2] = 42, foo[4] = 43".
+ * We can merge such operations into a single assignment op.  Essentially,
+ * the expression we want to produce in this case is like
+ *		foo = array_set(array_set(foo, 2, 42), 4, 43)
+ *
+ * 3. Sort the tlist into standard order: non-junk fields in order by resno,
+ * then junk fields (these in no particular order).
+ *
+ * We must do items 1 and 2 before firing rewrite rules, else rewritten
+ * references to NEW.foo will produce wrong or incomplete results.  Item 3
+ * is not needed for rewriting, but will be needed by the planner, and we
+ * can do it essentially for free while handling items 1 and 2.
+ */
+static void
+rewriteTargetList(Query *parsetree, Relation target_relation)
+{
+	CmdType		commandType = parsetree->commandType;
+	List	   *tlist = parsetree->targetList;
+	List	   *new_tlist = NIL;
+	int			attrno,
+				numattrs;
+	List	   *temp;
+
+	/*
+	 * Scan the tuple description in the relation's relcache entry to make
+	 * sure we have all the user attributes in the right order.
+	 */
+	numattrs = RelationGetNumberOfAttributes(target_relation);
+
+	for (attrno = 1; attrno <= numattrs; attrno++)
+	{
+		Form_pg_attribute att_tup = target_relation->rd_att->attrs[attrno-1];
+		TargetEntry *new_tle = NULL;
+
+		/*
+		 * Look for targetlist entries matching this attr.  We match by
+		 * resno, but the resname should match too.
+		 *
+		 * Junk attributes are not candidates to be matched.
+		 */
+		foreach(temp, tlist)
+		{
+			TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
+			Resdom	   *resdom = old_tle->resdom;
+
+			if (!resdom->resjunk && resdom->resno == attrno)
+			{
+				Assert(strcmp(resdom->resname,
+							  NameStr(att_tup->attname)) == 0);
+				new_tle = process_matched_tle(old_tle, new_tle);
+				/* keep scanning to detect multiple assignments to attr */
+			}
+		}
+
+		if (new_tle == NULL && commandType == CMD_INSERT)
+		{
+			/*
+			 * Didn't find a matching tlist entry; if it's an INSERT,
+			 * look for a default value, and add a tlist entry computing
+			 * the default if we find one.
+			 */
+			Node	   *new_expr;
+
+			new_expr = build_column_default(target_relation, attrno);
+
+			if (new_expr)
+				new_tle = makeTargetEntry(makeResdom(attrno,
+													 att_tup->atttypid,
+													 att_tup->atttypmod,
+													 pstrdup(NameStr(att_tup->attname)),
+													 false),
+										  new_expr);
+		}
+
+		if (new_tle)
+			new_tlist = lappend(new_tlist, new_tle);
+	}
+
+	/*
+	 * Copy all resjunk tlist entries to the end of the new tlist, and
+	 * assign them resnos above the last real resno.
+	 *
+	 * Typical junk entries include ORDER BY or GROUP BY expressions (are
+	 * these actually possible in an INSERT or UPDATE?), system attribute
+	 * references, etc.
+	 */
+	foreach(temp, tlist)
+	{
+		TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
+		Resdom	   *resdom = old_tle->resdom;
+
+		if (resdom->resjunk)
+		{
+			/* Get the resno right, but don't copy unnecessarily */
+			if (resdom->resno != attrno)
+			{
+				resdom = (Resdom *) copyObject((Node *) resdom);
+				resdom->resno = attrno;
+				old_tle = makeTargetEntry(resdom, old_tle->expr);
+			}
+			new_tlist = lappend(new_tlist, old_tle);
+			attrno++;
+		}
+		else
+		{
+			/* Let's just make sure we processed all the non-junk items */
+			if (resdom->resno < 1 || resdom->resno > numattrs)
+				elog(ERROR, "rewriteTargetList: bogus resno %d in targetlist",
+					 resdom->resno);
+		}
+	}
+
+	parsetree->targetList = new_tlist;
+}
+
+
+/*
+ * Convert a matched TLE from the original tlist into a correct new TLE.
+ *
+ * This routine detects and handles multiple assignments to the same target
+ * attribute.
+ */
+static TargetEntry *
+process_matched_tle(TargetEntry *src_tle,
+					TargetEntry *prior_tle)
+{
+	Resdom	   *resdom = src_tle->resdom;
+	Node	   *priorbottom;
+	ArrayRef   *newexpr;
+
+	if (prior_tle == NULL)
+	{
+		/*
+		 * Normal case where this is the first assignment to the
+		 * attribute.
+		 */
+		return src_tle;
+	}
+
+	/*
+	 * Multiple assignments to same attribute.	Allow only if all are
+	 * array-assign operators with same bottom array object.
+	 */
+	if (src_tle->expr == NULL || !IsA(src_tle->expr, ArrayRef) ||
+		((ArrayRef *) src_tle->expr)->refassgnexpr == NULL ||
+		prior_tle->expr == NULL || !IsA(prior_tle->expr, ArrayRef) ||
+		((ArrayRef *) prior_tle->expr)->refassgnexpr == NULL ||
+		((ArrayRef *) src_tle->expr)->refelemtype !=
+		((ArrayRef *) prior_tle->expr)->refelemtype)
+		elog(ERROR, "Multiple assignments to same attribute \"%s\"",
+			 resdom->resname);
+
+	/*
+	 * Prior TLE could be a nest of ArrayRefs if we do this more than
+	 * once.
+	 */
+	priorbottom = ((ArrayRef *) prior_tle->expr)->refexpr;
+	while (priorbottom != NULL && IsA(priorbottom, ArrayRef) &&
+		   ((ArrayRef *) priorbottom)->refassgnexpr != NULL)
+		priorbottom = ((ArrayRef *) priorbottom)->refexpr;
+	if (!equal(priorbottom, ((ArrayRef *) src_tle->expr)->refexpr))
+		elog(ERROR, "Multiple assignments to same attribute \"%s\"",
+			 resdom->resname);
+
+	/*
+	 * Looks OK to nest 'em.
+	 */
+	newexpr = makeNode(ArrayRef);
+	memcpy(newexpr, src_tle->expr, sizeof(ArrayRef));
+	newexpr->refexpr = prior_tle->expr;
+
+	return makeTargetEntry(resdom, (Node *) newexpr);
+}
+
+
+/*
+ * Make an expression tree for the default value for a column.
+ *
+ * If there is no default, return a NULL instead.
+ */
+static Node *
+build_column_default(Relation rel, int attrno)
+{
+	TupleDesc	rd_att = rel->rd_att;
+	Form_pg_attribute att_tup = rd_att->attrs[attrno - 1];
+	Oid			atttype = att_tup->atttypid;
+	int32		atttypmod = att_tup->atttypmod;
+	Node	   *expr = NULL;
+	Oid			exprtype;
+
+	/*
+	 * Scan to see if relation has a default for this column.
+	 */
+	if (rd_att->constr && rd_att->constr->num_defval > 0)
+	{
+		AttrDefault *defval = rd_att->constr->defval;
+		int			ndef = rd_att->constr->num_defval;
+
+		while (--ndef >= 0)
+		{
+			if (attrno == defval[ndef].adnum)
+			{
+				/*
+				 * Found it, convert string representation to node tree.
+				 */
+				expr = stringToNode(defval[ndef].adbin);
+				break;
+			}
+		}
+	}
+
+	if (expr == NULL)
+	{
+		/*
+		 * No per-column default, so look for a default for the type itself.
+		 */
+		if (att_tup->attisset)
+		{
+			/*
+			 * Set attributes are represented as OIDs no matter what the set
+			 * element type is, and the element type's default is irrelevant
+			 * too.
+			 */
+		}
+		else
+		{
+			expr = get_typdefault(atttype);
+		}
+	}
+
+	if (expr == NULL)
+		return NULL;			/* No default anywhere */
+
+	/*
+	 * Make sure the value is coerced to the target column
+	 * type (might not be right type yet if it's not a
+	 * constant!)  This should match the parser's processing of
+	 * non-defaulted expressions --- see
+	 * updateTargetListEntry().
+	 */
+	exprtype = exprType(expr);
+
+	if (exprtype != atttype)
+	{
+		expr = CoerceTargetExpr(NULL, expr, exprtype,
+								atttype, atttypmod);
+
+		/*
+		 * This really shouldn't fail; should have checked the
+		 * default's type when it was created ...
+		 */
+		if (expr == NULL)
+			elog(ERROR, "Column \"%s\" is of type %s"
+				 " but default expression is of type %s"
+				 "\n\tYou will need to rewrite or cast the expression",
+				 NameStr(att_tup->attname),
+				 format_type_be(atttype),
+				 format_type_be(exprtype));
+	}
+
+	/*
+	 * If the column is a fixed-length type, it may need a
+	 * length coercion as well as a type coercion.
+	 */
+	expr = coerce_type_typmod(NULL, expr, atttype, atttypmod);
+
+	return expr;
+}
+
+
 /*
  * matchLocks -
  *	  match the list of locks and returns the matching rules
@@ -764,6 +1053,14 @@ RewriteQuery(Query *parsetree, bool *instead_flag, List **qual_products)
 	 */
 	rt_entry_relation = heap_open(rt_entry->relid, RowExclusiveLock);
 
+	/*
+	 * If it's an INSERT or UPDATE, rewrite the targetlist into standard
+	 * form.  This will be needed by the planner anyway, and doing it now
+	 * ensures that any references to NEW.field will behave sanely.
+	 */
+	if (event == CMD_INSERT || event == CMD_UPDATE)
+		rewriteTargetList(parsetree, rt_entry_relation);
+
 	/*
 	 * Collect and apply the appropriate rules.
 	 */