diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 649c189ea97104f8804ba4a2e530cc56c400282d..a3eb943ab9614348509503c57357f2235b47d3f3 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.57 1999/07/17 20:16:57 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.58 1999/07/19 00:26:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -86,31 +86,44 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
 				 bool *isNull,
 				 bool *isDone)
 {
-	bool		dummy;
-	int			i = 0,
-				j = 0;
 	ArrayType  *array_scanner;
-	List	   *upperIndexpr,
-			   *lowerIndexpr;
-	Node	   *assgnexpr;
 	List	   *elt;
+	int			i = 0,
+				j = 0;
 	IntArray	upper,
 				lower;
 	int		   *lIndex;
-	char	   *dataPtr;
+	bool		dummy;
 
 	*isNull = false;
-	array_scanner = (ArrayType *) ExecEvalExpr(arrayRef->refexpr,
-											   econtext,
-											   isNull,
-											   isDone);
-	if (*isNull)
-		return (Datum) NULL;
 
-	upperIndexpr = arrayRef->refupperindexpr;
+	if (arrayRef->refexpr != NULL)
+	{
+		array_scanner = (ArrayType *) ExecEvalExpr(arrayRef->refexpr,
+												   econtext,
+												   isNull,
+												   isDone);
+		if (*isNull)
+			return (Datum) NULL;
+	}
+	else
+	{
+		/* Null refexpr indicates we are doing an INSERT into an array column.
+		 * For now, we just take the refassgnexpr (which the parser will have
+		 * ensured is an array value) and return it as-is, ignoring any
+		 * subscripts that may have been supplied in the INSERT column list.
+		 * This is a kluge, but it's not real clear what the semantics ought
+		 * to be...
+		 */
+		array_scanner = NULL;
+	}
 
-	foreach(elt, upperIndexpr)
+	foreach(elt, arrayRef->refupperindexpr)
 	{
+		if (i >= MAXDIM)
+			elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
+				 MAXDIM);
+
 		upper.indx[i++] = (int32) ExecEvalExpr((Node *) lfirst(elt),
 											   econtext,
 											   isNull,
@@ -119,12 +132,14 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
 			return (Datum) NULL;
 	}
 
-	lowerIndexpr = arrayRef->reflowerindexpr;
-	lIndex = NULL;
-	if (lowerIndexpr != NIL)
+	if (arrayRef->reflowerindexpr != NIL)
 	{
-		foreach(elt, lowerIndexpr)
+		foreach(elt, arrayRef->reflowerindexpr)
 		{
+			if (j >= MAXDIM)
+				elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
+					 MAXDIM);
+
 			lower.indx[j++] = (int32) ExecEvalExpr((Node *) lfirst(elt),
 												   econtext,
 												   isNull,
@@ -137,30 +152,42 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
 				 "ExecEvalArrayRef: upper and lower indices mismatch");
 		lIndex = lower.indx;
 	}
+	else
+	{
+		lIndex = NULL;
+	}
 
-	assgnexpr = arrayRef->refassgnexpr;
-	if (assgnexpr != NULL)
+	if (arrayRef->refassgnexpr != NULL)
 	{
-		dataPtr = (char *) ExecEvalExpr((Node *)
-										assgnexpr, econtext,
-										isNull, &dummy);
+		Datum sourceData = ExecEvalExpr(arrayRef->refassgnexpr,
+										econtext,
+										isNull,
+										&dummy);
 		if (*isNull)
 			return (Datum) NULL;
+
 		execConstByVal = arrayRef->refelembyval;
 		execConstLen = arrayRef->refelemlength;
+
+		if (array_scanner == NULL)
+			return sourceData;	/* XXX do something else? */
+
 		if (lIndex == NULL)
-			return (Datum) array_set(array_scanner, i, upper.indx, dataPtr,
+			return (Datum) array_set(array_scanner, i, upper.indx,
+									 (char *) sourceData,
 									 arrayRef->refelembyval,
 									 arrayRef->refelemlength,
 									 arrayRef->refattrlength, isNull);
 		return (Datum) array_assgn(array_scanner, i, upper.indx,
 								   lower.indx,
-								   (ArrayType *) dataPtr,
+								   (ArrayType *) sourceData,
 								   arrayRef->refelembyval,
 								   arrayRef->refelemlength, isNull);
 	}
+
 	execConstByVal = arrayRef->refelembyval;
 	execConstLen = arrayRef->refelemlength;
+
 	if (lIndex == NULL)
 		return (Datum) array_ref(array_scanner, i, upper.indx,
 								 arrayRef->refelembyval,
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 40a66b487389e4a6582d9796e1c67154acc686ff..d4a1ef57ef8dddecd50f85995ed63d744896e9d3 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -5,12 +5,11 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- *	$Id: analyze.c,v 1.115 1999/07/17 20:17:19 momjian Exp $
+ *	$Id: analyze.c,v 1.116 1999/07/19 00:26:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
-
 #include "postgres.h"
 
 #include "access/heapam.h"
@@ -38,8 +37,10 @@ static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
 static void transformForUpdate(Query *qry, List *forUpdate);
 void		CheckSelectForUpdate(Query *qry);
 
-List	   *extras_before = NIL;
-List	   *extras_after = NIL;
+/* kluge to return extra info from transformCreateStmt() */
+static List	   *extras_before;
+static List	   *extras_after;
+
 
 /*
  * parse_analyze -
@@ -58,17 +59,23 @@ parse_analyze(List *pl, ParseState *parentParseState)
 
 	while (pl != NIL)
 	{
+		extras_before = extras_after = NIL;
 		pstate = make_parsestate(parentParseState);
+
 		parsetree = transformStmt(pstate, lfirst(pl));
 		if (pstate->p_target_relation != NULL)
 			heap_close(pstate->p_target_relation);
+		pstate->p_target_relation = NULL;
+		pstate->p_target_rangetblentry = NULL;
 
 		while (extras_before != NIL)
 		{
 			result = lappend(result,
-						   transformStmt(pstate, lfirst(extras_before)));
+							 transformStmt(pstate, lfirst(extras_before)));
 			if (pstate->p_target_relation != NULL)
 				heap_close(pstate->p_target_relation);
+			pstate->p_target_relation = NULL;
+			pstate->p_target_rangetblentry = NULL;
 			extras_before = lnext(extras_before);
 		}
 
@@ -80,11 +87,13 @@ parse_analyze(List *pl, ParseState *parentParseState)
 							 transformStmt(pstate, lfirst(extras_after)));
 			if (pstate->p_target_relation != NULL)
 				heap_close(pstate->p_target_relation);
+			pstate->p_target_relation = NULL;
+			pstate->p_target_rangetblentry = NULL;
 			extras_after = lnext(extras_after);
 		}
 
-		pl = lnext(pl);
 		pfree(pstate);
+		pl = lnext(pl);
 	}
 
 	return result;
@@ -148,9 +157,9 @@ transformStmt(ParseState *pstate, Node *parseTree)
 				result->commandType = CMD_UTILITY;
 				result->utilityStmt = (Node *) parseTree;
 				MemoryContextSwitchTo(oldcontext);
-				break;
-
 			}
+			break;
+
 		case T_ExplainStmt:
 			{
 				ExplainStmt *n = (ExplainStmt *) parseTree;
@@ -215,7 +224,8 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
 	qry->commandType = CMD_DELETE;
 
 	/* set up a range table */
-	makeRangeTable(pstate, stmt->relname, NULL, NULL);
+	makeRangeTable(pstate, NULL, NULL);
+	setTargetTable(pstate, stmt->relname);
 
 	qry->uniqueFlag = NULL;
 
@@ -240,135 +250,70 @@ transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt)
 static Query *
 transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 {
-	Query	   *qry = makeNode(Query);	/* make a new query tree */
+	Query	   *qry = makeNode(Query);
+	Node	   *fromQual;
 	List	   *icolumns;
+	List	   *tl;
+	TupleDesc	rd_att;
 
 	qry->commandType = CMD_INSERT;
 	pstate->p_is_insert = true;
 
-	/* set up a range table */
-	makeRangeTable(pstate, stmt->relname, stmt->fromClause, NULL);
+	/*----------
+	 * Initial processing steps are just like SELECT, which should not
+	 * be surprising, since we may be handling an INSERT ... SELECT.
+	 * It is important that we finish processing all the SELECT subclauses
+	 * before we start doing any INSERT-specific processing; otherwise
+	 * the behavior of SELECT within INSERT might be different from a
+	 * stand-alone SELECT.  (Indeed, Postgres up through 6.5 had bugs of
+	 * just that nature...)
+	 *----------
+	 */
 
-	qry->uniqueFlag = stmt->unique;
+	/* set up a range table --- note INSERT target is not in it yet */
+	makeRangeTable(pstate, stmt->fromClause, &fromQual);
 
-	/* fix the target list */
-	icolumns = pstate->p_insert_columns = makeTargetNames(pstate, stmt->cols);
+	qry->uniqueFlag = stmt->unique;
 
 	qry->targetList = transformTargetList(pstate, stmt->targetList);
 
-	/* DEFAULT handling */
-	if (length(qry->targetList) < pstate->p_target_relation->rd_att->natts &&
-		pstate->p_target_relation->rd_att->constr &&
-		pstate->p_target_relation->rd_att->constr->num_defval > 0)
-	{
-		Form_pg_attribute *att = pstate->p_target_relation->rd_att->attrs;
-		AttrDefault *defval = pstate->p_target_relation->rd_att->constr->defval;
-		int			ndef = pstate->p_target_relation->rd_att->constr->num_defval;
-
-		/*
-		 * if stmt->cols == NIL then makeTargetNames returns list of all
-		 * attrs. May have to shorten icolumns list...
-		 */
-		if (stmt->cols == NIL)
-		{
-			List	   *extrl;
-			int			i = length(qry->targetList);
-
-			foreach(extrl, icolumns)
-			{
-
-				/*
-				 * decrements first, so if we started with zero items it
-				 * will now be negative
-				 */
-				if (--i <= 0)
-					break;
-			}
-
-			/*
-			 * this an index into the targetList, so make sure we had one
-			 * to start...
-			 */
-			if (i >= 0)
-			{
-				freeList(lnext(extrl));
-				lnext(extrl) = NIL;
-			}
-			else
-				icolumns = NIL;
-		}
+	qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual);
 
-		while (ndef-- > 0)
-		{
-			List	   *tl;
-			Ident	   *id;
-			TargetEntry *te;
-
-			foreach(tl, icolumns)
-			{
-				id = (Ident *) lfirst(tl);
-				if (namestrcmp(&(att[defval[ndef].adnum - 1]->attname), id->name) == 0)
-					break;
-			}
-			if (tl != NIL)		/* something given for this attr */
-				continue;
-
-			/*
-			 * Nothing given for this attr with DEFAULT expr, so add new
-			 * TargetEntry to qry->targetList. Note, that we set resno to
-			 * defval[ndef].adnum: it's what
-			 * transformTargetList()->make_targetlist_expr() does for
-			 * INSERT ... SELECT. But for INSERT ... VALUES
-			 * pstate->p_last_resno is used. It doesn't matter for
-			 * "normal" using (planner creates proper target list in
-			 * preptlist.c), but may break RULEs in some way. It seems
-			 * better to create proper target list here...
-			 */
-			te = makeTargetEntry(makeResdom(defval[ndef].adnum,
-								   att[defval[ndef].adnum - 1]->atttypid,
-								  att[defval[ndef].adnum - 1]->atttypmod,
-			   pstrdup(nameout(&(att[defval[ndef].adnum - 1]->attname))),
-											0, 0, false),
-							  (Node *) stringToNode(defval[ndef].adbin));
-			qry->targetList = lappend(qry->targetList, te);
-		}
-	}
-
-	/* fix where clause */
-	qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL);
-
-	/*
-	 * The havingQual has a similar meaning as "qual" in the where
-	 * statement. So we can easily use the code from the "where clause"
-	 * with some additional traversals done in
-	 * .../optimizer/plan/planner.c
+	/* Initial processing of HAVING clause is just like WHERE clause.
+	 * Additional work will be done in optimizer/plan/planner.c.
 	 */
 	qry->havingQual = transformWhereClause(pstate, stmt->havingClause, NULL);
 
-	qry->hasSubLinks = pstate->p_hasSubLinks;
-
-	/* now the range table will not change */
-	qry->rtable = pstate->p_rtable;
-	qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
-
 	qry->groupClause = transformGroupClause(pstate,
 											stmt->groupClause,
 											qry->targetList);
 
-	/* fix order clause */
+	/* An InsertStmt has no sortClause, but we still call
+	 * transformSortClause because it also handles uniqueFlag.
+	 */
 	qry->sortClause = transformSortClause(pstate,
 										  NIL,
 										  NIL,
 										  qry->targetList,
 										  qry->uniqueFlag);
 
+	qry->hasSubLinks = pstate->p_hasSubLinks;
 	qry->hasAggs = pstate->p_hasAggs;
 	if (pstate->p_hasAggs || qry->groupClause)
 		parseCheckAggregates(pstate, qry);
 
+	/*
+	 * If there is a havingQual but there are no aggregates, then there is
+	 * something wrong with the query because HAVING must contain
+	 * aggregates in its expressions! Otherwise the query could have been
+	 * formulated using the WHERE clause.
+	 */
+	if (qry->havingQual && ! qry->hasAggs)
+		elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
+
 	/*
 	 * The INSERT INTO ... SELECT ... could have a UNION in child, so
-	 * unionClause may be false ,
+	 * unionClause may be false
 	 */
 	qry->unionall = stmt->unionall;
 
@@ -380,15 +325,98 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 	qry->intersectClause = stmt->intersectClause;
 
 	/*
-	 * If there is a havingQual but there are no aggregates, then there is
-	 * something wrong with the query because having must contain
-	 * aggregates in its expressions! Otherwise the query could have been
-	 * formulated using the where clause.
+	 * Now we are done with SELECT-like processing, and can get on with
+	 * transforming the target list to match the INSERT target columns.
+	 *
+	 * In particular, it's time to add the INSERT target to the rangetable.
+	 * (We didn't want it there until now since it shouldn't be visible in
+	 * the SELECT part.)
+	 */
+	setTargetTable(pstate, stmt->relname);
+
+	/* now the range table will not change */
+	qry->rtable = pstate->p_rtable;
+	qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
+
+	/* Prepare to assign non-conflicting resnos to resjunk attributes */
+	if (pstate->p_last_resno <= pstate->p_target_relation->rd_rel->relnatts)
+		pstate->p_last_resno = pstate->p_target_relation->rd_rel->relnatts + 1;
+
+	/* Validate stmt->cols list, or build default list if no list given */
+	icolumns = makeTargetNames(pstate, stmt->cols);
+
+	/* Prepare non-junk columns for assignment to target table */
+	foreach(tl, qry->targetList)
+	{
+		TargetEntry *tle = (TargetEntry *) lfirst(tl);
+		Resdom	   *resnode = tle->resdom;
+		Ident	   *id;
+
+		if (resnode->resjunk)
+		{
+			/* Resjunk nodes need no additional processing, but be sure they
+			 * have names and resnos that do not match any target columns;
+			 * else rewriter or planner might get confused.
+			 */
+			resnode->resname = "?resjunk?";
+			resnode->resno = (AttrNumber) pstate->p_last_resno++;
+			continue;
+		}
+		if (icolumns == NIL)
+			elog(ERROR, "INSERT has more expressions than target columns");
+		id = (Ident *) lfirst(icolumns);
+		updateTargetListEntry(pstate, tle, id->name, id->indirection);
+		icolumns = lnext(icolumns);
+	}
+
+	/*
+	 * Add targetlist items to assign DEFAULT values to any columns that
+	 * have defaults and were not assigned to by the user.
+	 * XXX wouldn't it make more sense to do this further downstream,
+	 * after the rule rewriter?
 	 */
-	if ((qry->hasAggs == false) && (qry->havingQual != NULL))
+	rd_att = pstate->p_target_relation->rd_att;
+	if (rd_att->constr && rd_att->constr->num_defval > 0)
 	{
-		elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
-		return (Query *) NIL;
+		Form_pg_attribute *att = rd_att->attrs;
+		AttrDefault *defval = rd_att->constr->defval;
+		int			ndef = rd_att->constr->num_defval;
+
+		while (ndef-- > 0)
+		{
+			Form_pg_attribute thisatt = att[defval[ndef].adnum - 1];
+			TargetEntry *te;
+
+			foreach(tl, qry->targetList)
+			{
+				TargetEntry *tle = (TargetEntry *) lfirst(tl);
+				Resdom	   *resnode = tle->resdom;
+
+				if (resnode->resjunk)
+					continue;	/* ignore resjunk nodes */
+				if (namestrcmp(&(thisatt->attname), resnode->resname) == 0)
+					break;
+			}
+			if (tl != NIL)		/* something given for this attr */
+				continue;
+			/*
+			 * No user-supplied value, so add a targetentry with DEFAULT expr
+			 * and correct data for the target column.
+			 */
+			te = makeTargetEntry(
+				makeResdom(defval[ndef].adnum,
+						   thisatt->atttypid,
+						   thisatt->atttypmod,
+						   pstrdup(nameout(&(thisatt->attname))),
+						   0, 0, false),
+				stringToNode(defval[ndef].adbin));
+			qry->targetList = lappend(qry->targetList, te);
+			/*
+			 * Make sure the value is coerced to the target column type
+			 * (might not be right type if it's not a constant!)
+			 */
+			updateTargetListEntry(pstate, te, te->resdom->resname, NIL);
+		}
 	}
 
 	if (stmt->forUpdate != NULL)
@@ -963,12 +991,12 @@ static Query *
 transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 {
 	Query	   *qry = makeNode(Query);
-	Node	   *qual;
+	Node	   *fromQual;
 
 	qry->commandType = CMD_SELECT;
 
 	/* set up a range table */
-	makeRangeTable(pstate, NULL, stmt->fromClause, &qual);
+	makeRangeTable(pstate, stmt->fromClause, &fromQual);
 
 	qry->uniqueFlag = stmt->unique;
 
@@ -978,16 +1006,16 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 
 	qry->targetList = transformTargetList(pstate, stmt->targetList);
 
-	qry->qual = transformWhereClause(pstate, stmt->whereClause, qual);
+	qry->qual = transformWhereClause(pstate, stmt->whereClause, fromQual);
 
-	/*
-	 * The havingQual has a similar meaning as "qual" in the where
-	 * statement. So we can easily use the code from the "where clause"
-	 * with some additional traversals done in optimizer/plan/planner.c
+	/* Initial processing of HAVING clause is just like WHERE clause.
+	 * Additional work will be done in optimizer/plan/planner.c.
 	 */
 	qry->havingQual = transformWhereClause(pstate, stmt->havingClause, NULL);
 
-	qry->hasSubLinks = pstate->p_hasSubLinks;
+	qry->groupClause = transformGroupClause(pstate,
+											stmt->groupClause,
+											qry->targetList);
 
 	qry->sortClause = transformSortClause(pstate,
 										  stmt->sortClause,
@@ -995,15 +1023,20 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 										  qry->targetList,
 										  qry->uniqueFlag);
 
-	qry->groupClause = transformGroupClause(pstate,
-											stmt->groupClause,
-											qry->targetList);
-	qry->rtable = pstate->p_rtable;
-
+	qry->hasSubLinks = pstate->p_hasSubLinks;
 	qry->hasAggs = pstate->p_hasAggs;
 	if (pstate->p_hasAggs || qry->groupClause)
 		parseCheckAggregates(pstate, qry);
 
+	/*
+	 * If there is a havingQual but there are no aggregates, then there is
+	 * something wrong with the query because HAVING must contain
+	 * aggregates in its expressions! Otherwise the query could have been
+	 * formulated using the WHERE clause.
+	 */
+	if (qry->havingQual && ! qry->hasAggs)
+		elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
+
 	/*
 	 * The INSERT INTO ... SELECT ... could have a UNION in child, so
 	 * unionClause may be false
@@ -1017,17 +1050,7 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 	qry->unionClause = stmt->unionClause;
 	qry->intersectClause = stmt->intersectClause;
 
-	/*
-	 * If there is a havingQual but there are no aggregates, then there is
-	 * something wrong with the query because having must contain
-	 * aggregates in its expressions! Otherwise the query could have been
-	 * formulated using the where clause.
-	 */
-	if ((qry->hasAggs == false) && (qry->havingQual != NULL))
-	{
-		elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
-		return (Query *) NIL;
-	}
+	qry->rtable = pstate->p_rtable;
 
 	if (stmt->forUpdate != NULL)
 		transformForUpdate(qry, stmt->forUpdate);
@@ -1044,6 +1067,8 @@ static Query *
 transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 {
 	Query	   *qry = makeNode(Query);
+	List	   *origTargetList;
+	List	   *tl;
 
 	qry->commandType = CMD_UPDATE;
 	pstate->p_is_update = true;
@@ -1052,21 +1077,59 @@ transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt)
 	 * the FROM clause is non-standard SQL syntax. We used to be able to
 	 * do this with REPLACE in POSTQUEL so we keep the feature.
 	 */
-	makeRangeTable(pstate, stmt->relname, stmt->fromClause, NULL);
+	makeRangeTable(pstate, stmt->fromClause, NULL);
+	setTargetTable(pstate, stmt->relname);
 
 	qry->targetList = transformTargetList(pstate, stmt->targetList);
 
 	qry->qual = transformWhereClause(pstate, stmt->whereClause, NULL);
+
 	qry->hasSubLinks = pstate->p_hasSubLinks;
 
 	qry->rtable = pstate->p_rtable;
-
 	qry->resultRelation = refnameRangeTablePosn(pstate, stmt->relname, NULL);
 
 	qry->hasAggs = pstate->p_hasAggs;
 	if (pstate->p_hasAggs)
 		parseCheckAggregates(pstate, qry);
 
+	/*
+	 * Now we are done with SELECT-like processing, and can get on with
+	 * transforming the target list to match the UPDATE target columns.
+	 */
+
+	/* Prepare to assign non-conflicting resnos to resjunk attributes */
+	if (pstate->p_last_resno <= pstate->p_target_relation->rd_rel->relnatts)
+		pstate->p_last_resno = pstate->p_target_relation->rd_rel->relnatts + 1;
+
+	/* Prepare non-junk columns for assignment to target table */
+	origTargetList = stmt->targetList;
+	foreach(tl, qry->targetList)
+	{
+		TargetEntry *tle = (TargetEntry *) lfirst(tl);
+		Resdom	   *resnode = tle->resdom;
+		ResTarget  *origTarget;
+
+		if (resnode->resjunk)
+		{
+			/* Resjunk nodes need no additional processing, but be sure they
+			 * have names and resnos that do not match any target columns;
+			 * else rewriter or planner might get confused.
+			 */
+			resnode->resname = "?resjunk?";
+			resnode->resno = (AttrNumber) pstate->p_last_resno++;
+			continue;
+		}
+		if (origTargetList == NIL)
+			elog(ERROR, "UPDATE target count mismatch --- internal error");
+		origTarget = (ResTarget *) lfirst(origTargetList);
+		updateTargetListEntry(pstate, tle,
+							  origTarget->name, origTarget->indirection);
+		origTargetList = lnext(origTargetList);
+	}
+	if (origTargetList != NIL)
+		elog(ERROR, "UPDATE target count mismatch --- internal error");
+
 	return (Query *) qry;
 }
 
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index ce9583c174a23b9190921aa4e0442db5ca750887..f8dc7777b0609b881f2cfe428267bc3960055b06 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -7,12 +7,13 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.41 1999/07/17 20:17:23 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.42 1999/07/19 00:26:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
+
 #include "access/heapam.h"
 #include "nodes/relation.h"
 #include "parse.h"
@@ -29,34 +30,39 @@
 
 static char *clauseText[] = {"ORDER", "GROUP"};
 
-static TargetEntry *
-			findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause);
-
+static TargetEntry *findTargetlistEntry(ParseState *pstate, Node *node,
+										List *tlist, int clause);
 static void parseFromClause(ParseState *pstate, List *frmList, Node **qual);
+static char	*transformTableEntry(ParseState *pstate, RangeVar *r);
 
 #ifdef ENABLE_OUTER_JOINS
-Node	   *transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname);
-
+static Node *transformUsingClause(ParseState *pstate, List *onList,
+								  char *lname, char *rname);
 #endif
 
-static char	*transformTableEntry(ParseState *pstate, RangeVar *r);
 
 
 /*
  * makeRangeTable -
- *	  make a range table with the specified relation (optional) and the
- *	  from_clause.
+ *	  Build the initial range table from the FROM clause.
  */
 void
-makeRangeTable(ParseState *pstate, char *relname, List *frmList, Node **qual)
+makeRangeTable(ParseState *pstate, List *frmList, Node **qual)
 {
-	RangeTblEntry *rte;
-	int			sublevels_up;
-
+	/* Currently, nothing to do except this: */
 	parseFromClause(pstate, frmList, qual);
+}
 
-	if (relname == NULL)
-		return;
+/*
+ * setTargetTable
+ *	  Add the target relation of INSERT or UPDATE to the range table,
+ *	  and make the special links to it in the ParseState.
+ */
+void
+setTargetTable(ParseState *pstate, char *relname)
+{
+	RangeTblEntry *rte;
+	int			sublevels_up;
 
 	if ((refnameRangeTablePosn(pstate, relname, &sublevels_up) == 0)
 		|| (sublevels_up != 0))
@@ -136,7 +142,7 @@ makeAttr(char *relname, char *attname)
 /* transformUsingClause()
  * Take an ON or USING clause from a join expression and expand if necessary.
  */
-Node *
+static Node *
 transformUsingClause(ParseState *pstate, List *onList, char *lname, char *rname)
 {
 	A_Expr	   *expr = NULL;
@@ -295,7 +301,8 @@ parseFromClause(ParseState *pstate, List *frmList, Node **qual)
 				if (IsA(j->quals, List))
 					j->quals = lcons(transformUsingClause(pstate, (List *) j->quals, lname, rname), NIL);
 
-				Assert(qual != NULL);
+				if (qual == NULL)
+					elog(ERROR, "JOIN/ON not supported in this context");
 
 				if (*qual == NULL)
 					*qual = lfirst(j->quals);
@@ -329,145 +336,111 @@ parseFromClause(ParseState *pstate, List *frmList, Node **qual)
 	}
 }
 
+
 /*
  *	findTargetlistEntry -
- *	  returns the Resdom in the target list matching the specified varname
- *	  and range. If none exist one is created.
- *
- *	  Rewritten for ver 6.4 to handle expressions in the GROUP/ORDER BY clauses.
- *	   - daveh@insightdist.com	1998-07-31
+ *	  Returns the targetlist entry matching the given (untransformed) node.
+ *	  If no matching entry exists, one is created and appended to the target
+ *	  list as a "resjunk" node.
  *
+ * node		the ORDER BY or GROUP BY expression to be matched
+ * tlist	the existing target list (NB: this cannot be NIL, which is a
+ *			good thing since we'd be unable to append to it...)
+ * clause	identifies clause type for error messages.
  */
 static TargetEntry *
 findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
 {
-	List	   *l;
-	int			rtable_pos = 0,
-				target_pos = 0,
-				targetlist_pos = 0;
 	TargetEntry *target_result = NULL;
-	Value	   *val = NULL;
-	char	   *relname = NULL;
-	char	   *name = NULL;
-	Node	   *expr = NULL;
-	int			relCnt = 0;
-
-	/* Pull out some values before looping thru target list  */
-	switch (nodeTag(node))
-	{
-		case T_Attr:
-			relname = ((Attr *) node)->relname;
-			val = (Value *) lfirst(((Attr *) node)->attrs);
-			name = strVal(val);
-			rtable_pos = refnameRangeTablePosn(pstate, relname, NULL);
-			relCnt = length(pstate->p_rtable);
-			break;
-
-		case T_Ident:
-			name = ((Ident *) node)->name;
-			relCnt = length(pstate->p_rtable);
-			break;
-
-		case T_A_Const:
-			val = &((A_Const *) node)->val;
-
-			if (nodeTag(val) != T_Integer)
-				elog(ERROR, "Illegal Constant in %s BY", clauseText[clause]);
-			target_pos = intVal(val);
-			break;
-
-		case T_FuncCall:
-		case T_A_Expr:
-			expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
-			break;
-
-		default:
-			elog(ERROR, "Illegal %s BY node = %d", clauseText[clause], nodeTag(node));
-	}
+	List	   *tl;
+	Node	   *expr;
 
-	/*
-	 * Loop through target entries and try to match to node
+	/*----------
+	 * Handle two special cases as mandated by the SQL92 spec:
+	 *
+	 * 1. ORDER/GROUP BY ColumnName
+	 *    For a bare identifier, we search for a matching column name
+	 *	  in the existing target list.  Multiple matches are an error
+	 *	  unless they refer to identical values; for example,
+	 *	  we allow  SELECT a, a FROM table ORDER BY a
+	 *	  but not   SELECT a AS b, b FROM table ORDER BY b
+	 *	  If no match is found, we fall through and treat the identifier
+	 *	  as an expression.
+	 *
+	 * 2. ORDER/GROUP BY IntegerConstant
+	 *	  This means to use the n'th item in the existing target list.
+	 *	  Note that it would make no sense to order/group by an actual
+	 *	  constant, so this does not create a conflict with our extension
+	 *	  to order/group by an expression.
+	 *
+	 * Note that pre-existing resjunk targets must not be used in either case.
+	 *----------
 	 */
-	foreach(l, tlist)
+	if (IsA(node, Ident) && ((Ident *) node)->indirection == NIL)
 	{
-		TargetEntry *target = (TargetEntry *) lfirst(l);
-		Resdom	   *resnode = target->resdom;
-		Var		   *var = (Var *) target->expr;
-		char	   *resname = resnode->resname;
-		int			test_rtable_pos = var->varno;
-
-		++targetlist_pos;
-
-		switch (nodeTag(node))
+		char	   *name = ((Ident *) node)->name;
+		foreach(tl, tlist)
 		{
-			case T_Attr:
-				if (strcmp(resname, name) == 0 && rtable_pos == test_rtable_pos)
-				{
-
-					/*
-					 * Check for only 1 table & ORDER BY -ambiguity does
-					 * not matter here
-					 */
-					if (clause == ORDER_CLAUSE && relCnt == 1)
-						return target;
-
-					if (target_result != NULL)
-						elog(ERROR, "%s BY '%s' is ambiguous", clauseText[clause], name);
-					else
-						target_result = target;
-					/* Stay in loop to check for ambiguity */
-				}
-				break;
-
-			case T_Ident:
-				if (strcmp(resname, name) == 0)
-				{
+			TargetEntry *tle = (TargetEntry *) lfirst(tl);
+			Resdom	   *resnode = tle->resdom;
 
-					/*
-					 * Check for only 1 table & ORDER BY  -ambiguity does
-					 * not matter here
-					 */
-					if (clause == ORDER_CLAUSE && relCnt == 1)
-						return target;
-
-					if (target_result != NULL)
-						elog(ERROR, "%s BY '%s' is ambiguous", clauseText[clause], name);
-					else
-						target_result = target;
-					/* Stay in loop to check for ambiguity	*/
-				}
-				break;
-
-			case T_A_Const:
-				if (target_pos == targetlist_pos)
+			if (!resnode->resjunk &&
+				strcmp(resnode->resname, name) == 0)
+			{
+				if (target_result != NULL)
 				{
-					/* Can't be ambigious and we got what we came for  */
-					return target;
+					if (! equal(target_result->expr, tle->expr))
+						elog(ERROR, "%s BY '%s' is ambiguous",
+							 clauseText[clause], name);
 				}
-				break;
+				else
+					target_result = tle;
+				/* Stay in loop to check for ambiguity */
+			}
+		}
+		if (target_result != NULL)
+			return target_result; /* return the first match */
+	}
+	if (IsA(node, A_Const))
+	{
+		Value	   *val = &((A_Const *) node)->val;
+		int			targetlist_pos = 0;
+		int			target_pos;
+
+		if (nodeTag(val) != T_Integer)
+			elog(ERROR, "Non-integer constant in %s BY", clauseText[clause]);
+		target_pos = intVal(val);
+		foreach(tl, tlist)
+		{
+			TargetEntry *tle = (TargetEntry *) lfirst(tl);
+			Resdom	   *resnode = tle->resdom;
 
-			case T_FuncCall:
-			case T_A_Expr:
-				if (equal(expr, target->expr))
-				{
+			if (!resnode->resjunk)
+			{
+				if (++targetlist_pos == target_pos)
+					return tle;	/* return the unique match */
+			}
+		}
+		elog(ERROR, "%s BY position %d is not in target list",
+			 clauseText[clause], target_pos);
+	}
 
-					/*
-					 * Check for only 1 table & ORDER BY  -ambiguity does
-					 * not matter here
-					 */
-					if (clause == ORDER_CLAUSE)
-						return target;
+	/*
+	 * Otherwise, we have an expression (this is a Postgres extension
+	 * not found in SQL92).  Convert the untransformed node to a
+	 * transformed expression, and search for a match in the tlist.
+	 * NOTE: it doesn't really matter whether there is more than one
+	 * match.  Also, we are willing to match a resjunk target here,
+	 * though the above cases must ignore resjunk targets.
+	 */
+	expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
 
-					if (target_result != NULL)
-						elog(ERROR, "GROUP BY has ambiguous expression");
-					else
-						target_result = target;
-				}
-				break;
+	foreach(tl, tlist)
+	{
+		TargetEntry *tle = (TargetEntry *) lfirst(tl);
 
-			default:
-				elog(ERROR, "Illegal %s BY node = %d", clauseText[clause], nodeTag(node));
-		}
+		if (equal(expr, tle->expr))
+			return tle;
 	}
 
 	/*
@@ -475,51 +448,13 @@ findTargetlistEntry(ParseState *pstate, Node *node, List *tlist, int clause)
 	 * the end of the target list.	 This target is set to be  resjunk =
 	 * TRUE so that it will not be projected into the final tuple.
 	 */
-	if (target_result == NULL)
-	{
-		switch (nodeTag(node))
-		{
-			case T_Attr:
-				target_result = MakeTargetEntryIdent(pstate, node,
-										 &((Attr *) node)->relname, NULL,
-										 ((Attr *) node)->relname, true);
-				lappend(tlist, target_result);
-				break;
-
-			case T_Ident:
-				target_result = MakeTargetEntryIdent(pstate, node,
-										   &((Ident *) node)->name, NULL,
-										   ((Ident *) node)->name, true);
-				lappend(tlist, target_result);
-				break;
-
-			case T_A_Const:
-
-				/*
-				 * If we got this far, then must have been an out-of-range
-				 * column number
-				 */
-				elog(ERROR, "%s BY position %d is not in target list", clauseText[clause], target_pos);
-				break;
-
-			case T_FuncCall:
-			case T_A_Expr:
-				target_result = MakeTargetEntryExpr(pstate, "resjunk", expr, false, true);
-				lappend(tlist, target_result);
-				break;
-
-			default:
-				elog(ERROR, "Illegal %s BY node = %d", clauseText[clause], nodeTag(node));
-				break;
-		}
-	}
+	target_result = transformTargetEntry(pstate, node, expr, NULL, true);
+	lappend(tlist, target_result);
 
 	return target_result;
 }
 
 
-
-
 /*
  * transformGroupClause -
  *	  transform a Group By clause
@@ -529,55 +464,42 @@ List *
 transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
 {
 	List	   *glist = NIL,
-			   *gl = NIL;
+			   *gl,
+			   *othergl;
+	int			nextgroupref = 1;
 
-	while (grouplist != NIL)
+	foreach(gl, grouplist)
 	{
-		GroupClause *grpcl = makeNode(GroupClause);
 		TargetEntry *restarget;
 		Resdom	   *resdom;
 
-		restarget = findTargetlistEntry(pstate, lfirst(grouplist), targetlist, GROUP_CLAUSE);
-
+		restarget = findTargetlistEntry(pstate, lfirst(gl),
+										targetlist, GROUP_CLAUSE);
 		resdom = restarget->resdom;
-		grpcl->grpOpoid = oprid(oper("<",
-									 resdom->restype,
-									 resdom->restype, false));
-		if (glist == NIL)
-		{
-			int			groupref = length(glist) + 1;
 
-			restarget->resdom->resgroupref = groupref;
-			grpcl->tleGroupref = groupref;
+		/* avoid making duplicate grouplist entries */
+		foreach(othergl, glist)
+		{
+			GroupClause *gcl = (GroupClause *) lfirst(othergl);
 
-			gl = glist = lcons(grpcl, NIL);
+			if (equal(get_groupclause_expr(gcl, targetlist),
+					  restarget->expr))
+				break;
 		}
-		else
-		{
-			List	   *i;
 
-			foreach(i, glist)
-			{
-				GroupClause *gcl = (GroupClause *) lfirst(i);
+		if (othergl == NIL)		/* not in grouplist already */
+		{
+			GroupClause *grpcl = makeNode(GroupClause);
 
-				if (equal(get_groupclause_expr(gcl, targetlist),
-						  restarget->expr))
-					break;
-			}
-			if (i == NIL)		/* not in grouplist already */
-			{
-				int			groupref = length(glist) + 1;
+			grpcl->tleGroupref = nextgroupref++;
+			resdom->resgroupref = grpcl->tleGroupref;
 
-				restarget->resdom->resgroupref = groupref;
-				grpcl->tleGroupref = groupref;
+			grpcl->grpOpoid = oprid(oper("<",
+										 resdom->restype,
+										 resdom->restype, false));
 
-				lnext(gl) = lcons(grpcl, NIL);
-				gl = lnext(gl);
-			}
-			else
-				pfree(grpcl);	/* get rid of this */
+			glist = lappend(glist, grpcl);
 		}
-		grouplist = lnext(grouplist);
 	}
 
 	return glist;
@@ -604,7 +526,8 @@ transformSortClause(ParseState *pstate,
 		TargetEntry *restarget;
 		Resdom	   *resdom;
 
-		restarget = findTargetlistEntry(pstate, sortby->node, targetlist, ORDER_CLAUSE);
+		restarget = findTargetlistEntry(pstate, sortby->node,
+										targetlist, ORDER_CLAUSE);
 
 		sortcl->resdom = resdom = restarget->resdom;
 
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index a936554ad9d221343ab937b32525541842e2085a..e9a6745334242cf082540109c71c22b90acc208f 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.54 1999/07/17 20:17:23 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.55 1999/07/19 00:26:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,7 +30,7 @@ static Node *parser_typecast(Value *expr, TypeName *typename, int32 atttypmod);
 static Node *transformAttr(ParseState *pstate, Attr *att, int precedence);
 static Node *transformIdent(ParseState *pstate, Ident *ident, int precedence);
 static Node *transformIndirection(ParseState *pstate, Node *basenode,
-								  List *indirection, int precedence);
+								  List *indirection);
 
 /*
  * transformExpr -
@@ -81,7 +81,7 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 				param->paramtype = (Oid) toid;
 				param->param_tlist = (List *) NULL;
 				result = transformIndirection(pstate, (Node *) param,
-											  pno->indirection, precedence);
+											  pno->indirection);
 				break;
 			}
 		case T_A_Expr:
@@ -467,37 +467,12 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
 }
 
 static Node *
-transformIndirection(ParseState *pstate, Node *basenode,
-					 List *indirection, int precedence)
+transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
 {
-	List	   *idx;
-
 	if (indirection == NIL)
 		return basenode;
-	foreach (idx, indirection)
-	{
-		A_Indices  *ai = (A_Indices *) lfirst(idx);
-		Node	   *lexpr = NULL,
-				   *uexpr;
-
-		/* uidx is always present, but lidx might be null */
-		if (ai->lidx != NULL)
-		{
-			lexpr = transformExpr(pstate, ai->lidx, precedence);
-			if (exprType(lexpr) != INT4OID)
-				elog(ERROR, "array index expressions must be int4's");
-		}
-		uexpr = transformExpr(pstate, ai->uidx, precedence);
-		if (exprType(uexpr) != INT4OID)
-			elog(ERROR, "array index expressions must be int4's");
-		ai->lidx = lexpr;
-		ai->uidx = uexpr;
-		/*
-		 * note we reuse the list of A_Indices nodes, make sure
-		 * we don't free them! Otherwise, make a new list here
-		 */
-	}
-	return (Node *) make_array_ref(basenode, indirection);
+	return (Node *) transformArraySubscripts(pstate, basenode,
+											 indirection, false, NULL);
 }
 
 static Node *
@@ -505,11 +480,9 @@ transformAttr(ParseState *pstate, Attr *att, int precedence)
 {
 	Node	   *basenode;
 
-	/* what if att->attrs == "*"? */
 	basenode = ParseNestedFuncOrColumn(pstate, att, &pstate->p_last_resno,
 									   precedence);
-	return transformIndirection(pstate, basenode,
-								att->indirection, precedence);
+	return transformIndirection(pstate, basenode, att->indirection);
 }
 
 static Node *
@@ -555,7 +528,7 @@ transformIdent(ParseState *pstate, Ident *ident, int precedence)
 Oid
 exprType(Node *expr)
 {
-	Oid			type = (Oid) 0;
+	Oid			type = (Oid) InvalidOid;
 
 	if (!expr)
 		return type;
@@ -622,6 +595,42 @@ exprType(Node *expr)
 	return type;
 }
 
+/*
+ *	exprTypmod -
+ *	  returns the type-specific attrmod of the expression, if it can be
+ *	  determined.  In most cases, it can't and we return -1.
+ */
+int32
+exprTypmod(Node *expr)
+{
+	if (!expr)
+		return -1;
+
+	switch (nodeTag(expr))
+	{
+		case T_Var:
+			return ((Var *) expr)->vartypmod;
+		case T_Const:
+			{
+				/* Be smart about string constants... */
+				Const  *con = (Const *) expr;
+				switch (con->consttype)
+				{
+					case BPCHAROID:
+						if (! con->constisnull)
+							return VARSIZE(DatumGetPointer(con->constvalue));
+						break;
+					default:
+						break;
+				}
+			}
+			break;
+		default:
+			break;
+	}
+	return -1;
+}
+
 static Node *
 parser_typecast(Value *expr, TypeName *typename, int32 atttypmod)
 {
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 53c756670c34b61390711cbc0e4674f94108fc44..80a8543d5a5addf8d9fe871bda674a4e0492a90d 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.28 1999/07/17 20:17:24 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.29 1999/07/19 00:26:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,6 +24,7 @@
 #include "parser/parse_node.h"
 #include "parser/parse_oper.h"
 #include "parser/parse_relation.h"
+#include "parser/parse_target.h"
 #include "parser/parse_type.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
@@ -222,164 +223,184 @@ make_var(ParseState *pstate, Oid relid, char *refname,
 }
 
 /*
- *	make_array_ref() -- Make an array reference node.
+ * transformArraySubscripts()
+ *		Transform array subscripting.  This is used for both
+ *		array fetch and array assignment.
  *
- *		Array references can hang off of arbitrary nested dot (or
- *		function invocation) expressions.  This routine takes a
- *		tree generated by ParseFunc() and an array index and
- *		generates a new array reference tree.  We do some simple
- *		typechecking to be sure the dereference is valid in the
- *		type system, but we don't do any bounds checking here.
+ * In an array fetch, we are given a source array value and we produce an
+ * expression that represents the result of extracting a single array element
+ * or an array slice.
  *
- *	indirection is a list of A_Indices
+ * In an array assignment, we are given a destination array value plus a
+ * source value that is to be assigned to a single element or a slice of
+ * that array.  We produce an expression that represents the new array value
+ * with the source data inserted into the right part of the array.
+ *
+ * pstate		Parse state
+ * arrayBase	Already-transformed expression for the array as a whole
+ * indirection	Untransformed list of subscripts (must not be NIL)
+ * forceSlice	If true, treat subscript as array slice in all cases
+ * assignFrom	NULL for array fetch, else transformed expression for source.
  */
-ArrayRef   *
-make_array_ref(Node *expr,
-			   List *indirection)
+ArrayRef *
+transformArraySubscripts(ParseState *pstate,
+						 Node *arrayBase,
+						 List *indirection,
+						 bool forceSlice,
+						 Node *assignFrom)
 {
-	Oid			typearray;
+	Oid			typearray,
+				typeelement,
+				typeresult;
 	HeapTuple	type_tuple;
 	Form_pg_type type_struct_array,
 				type_struct_element;
-	ArrayRef   *aref;
-	Oid			reftype;
+	bool		isSlice = forceSlice;
 	List	   *upperIndexpr = NIL;
 	List	   *lowerIndexpr = NIL;
+	List	   *idx;
+	ArrayRef   *aref;
 
-	typearray = exprType(expr);
+	/* Get the type tuple for the array */
+	typearray = exprType(arrayBase);
 
 	type_tuple = SearchSysCacheTuple(TYPOID,
 									 ObjectIdGetDatum(typearray),
 									 0, 0, 0);
-
 	if (!HeapTupleIsValid(type_tuple))
-		elog(ERROR, "make_array_ref: Cache lookup failed for type %u\n",
+		elog(ERROR, "transformArraySubscripts: Cache lookup failed for array type %u",
 			 typearray);
-
-	/* get the array type struct from the type tuple */
 	type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple);
 
-	if (type_struct_array->typelem == InvalidOid)
-		elog(ERROR, "make_array_ref: type %s is not an array",
+	typeelement = type_struct_array->typelem;
+	if (typeelement == InvalidOid)
+		elog(ERROR, "transformArraySubscripts: type %s is not an array",
 			 type_struct_array->typname);
 
-	/* get the type tuple for the element type */
+	/* Get the type tuple for the array element type */
 	type_tuple = SearchSysCacheTuple(TYPOID,
-							ObjectIdGetDatum(type_struct_array->typelem),
+									 ObjectIdGetDatum(typeelement),
 									 0, 0, 0);
 	if (!HeapTupleIsValid(type_tuple))
-		elog(ERROR, "make_array_ref: Cache lookup failed for type %u\n",
-			 typearray);
-
+		elog(ERROR, "transformArraySubscripts: Cache lookup failed for array element type %u",
+			 typeelement);
 	type_struct_element = (Form_pg_type) GETSTRUCT(type_tuple);
 
-	while (indirection != NIL)
+	/*
+	 * A list containing only single subscripts refers to a single array
+	 * element.  If any of the items are double subscripts (lower:upper),
+	 * then the subscript expression means an array slice operation.
+	 * In this case, we supply a default lower bound of 1 for any items
+	 * that contain only a single subscript.
+	 * The forceSlice parameter forces us to treat the operation as a
+	 * slice, even if no lower bounds are mentioned.  Otherwise,
+	 * we have to prescan the indirection list to see if there are any
+	 * double subscripts.
+	 */
+	if (! isSlice)
 	{
-		A_Indices  *ind = lfirst(indirection);
-
-		if (ind->lidx)
-
-			/*
-			 * XXX assumes all lower indices non null in this case
-			 */
-			lowerIndexpr = lappend(lowerIndexpr, ind->lidx);
-
-		upperIndexpr = lappend(upperIndexpr, ind->uidx);
-		indirection = lnext(indirection);
+		foreach (idx, indirection)
+		{
+			A_Indices  *ai = (A_Indices *) lfirst(idx);
+			if (ai->lidx != NULL)
+			{
+				isSlice = true;
+				break;
+			}
+		}
 	}
-	aref = makeNode(ArrayRef);
-	aref->refattrlength = type_struct_array->typlen;
-	aref->refelemlength = type_struct_element->typlen;
-	aref->refelemtype = type_struct_array->typelem;
-	aref->refelembyval = type_struct_element->typbyval;
-	aref->refupperindexpr = upperIndexpr;
-	aref->reflowerindexpr = lowerIndexpr;
-	aref->refexpr = expr;
-	aref->refassgnexpr = NULL;
 
-	if (lowerIndexpr == NIL)	/* accessing a single array element */
-		reftype = aref->refelemtype;
+	/* The type represented by the subscript expression is the element type
+	 * if we are fetching a single element, but it is the same as the array
+	 * type if we are fetching a slice or storing.
+	 */
+	if (isSlice || assignFrom != NULL)
+		typeresult = typearray;
 	else
-/* request to clip a part of the array, the result is another array */
-		reftype = typearray;
+		typeresult = typeelement;
 
 	/*
-	 * we change it to reflect the true type; since the original
-	 * refelemtype doesn't seem to get used anywhere. - ay 10/94
+	 * Transform the subscript expressions.
 	 */
-	aref->refelemtype = reftype;
-
-	return aref;
-}
-
-
-/* make_array_set()
- */
-ArrayRef   *
-make_array_set(Expr *target_expr,
-			   List *upperIndexpr,
-			   List *lowerIndexpr,
-			   Expr *expr)
-{
-	Oid			typearray;
-	HeapTuple	type_tuple;
-	Form_pg_type type_struct_array;
-	Form_pg_type type_struct_element;
-	ArrayRef   *aref;
-	Oid			reftype;
-
-	typearray = exprType((Node *) target_expr);
-
-	type_tuple = SearchSysCacheTuple(TYPOID,
-									 ObjectIdGetDatum(typearray),
-									 0, 0, 0);
-
-	if (!HeapTupleIsValid(type_tuple))
-		elog(ERROR, "make_array_ref: Cache lookup failed for type %u\n",
-			 typearray);
-
-	/* get the array type struct from the type tuple */
-	type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple);
+	foreach (idx, indirection)
+	{
+		A_Indices  *ai = (A_Indices *) lfirst(idx);
+		Node	   *subexpr;
 
-	if (type_struct_array->typelem == InvalidOid)
-		elog(ERROR, "make_array_ref: type %s is not an array",
-			 type_struct_array->typname);
-	/* get the type tuple for the element type */
-	type_tuple = SearchSysCacheTuple(TYPOID,
-							ObjectIdGetDatum(type_struct_array->typelem),
-									 0, 0, 0);
+		if (isSlice)
+		{
+			if (ai->lidx)
+			{
+				subexpr = transformExpr(pstate, ai->lidx, EXPR_COLUMN_FIRST);
+				/* If it's not int4 already, try to coerce */
+				subexpr = CoerceTargetExpr(pstate, subexpr,
+										   exprType(subexpr), INT4OID);
+				if (subexpr == NULL)
+					elog(ERROR, "array index expressions must be integers");
+			}
+			else
+			{
+				/* Make a constant 1 */
+				subexpr = (Node *) makeConst(INT4OID,
+											 sizeof(int32),
+											 Int32GetDatum(1),
+											 false,
+											 true, /* pass by value */
+											 false,
+											 false);
+			}
+			lowerIndexpr = lappend(lowerIndexpr, subexpr);
+		}
+		subexpr = transformExpr(pstate, ai->uidx, EXPR_COLUMN_FIRST);
+		/* If it's not int4 already, try to coerce */
+		subexpr = CoerceTargetExpr(pstate, subexpr,
+								   exprType(subexpr), INT4OID);
+		if (subexpr == NULL)
+			elog(ERROR, "array index expressions must be integers");
+		upperIndexpr = lappend(upperIndexpr, subexpr);
+	}
 
-	if (!HeapTupleIsValid(type_tuple))
-		elog(ERROR, "make_array_ref: Cache lookup failed for type %u\n",
-			 typearray);
+	/*
+	 * If doing an array store, coerce the source value to the right type.
+	 */
+	if (assignFrom != NULL)
+	{
+		Oid			typesource = exprType(assignFrom);
+		Oid			typeneeded = isSlice ? typearray : typeelement;
 
-	type_struct_element = (Form_pg_type) GETSTRUCT(type_tuple);
+		if (typesource != InvalidOid)
+		{
+			if (typesource != typeneeded)
+			{
+				assignFrom = CoerceTargetExpr(pstate, assignFrom,
+											  typesource, typeneeded);
+				if (assignFrom == NULL)
+					elog(ERROR, "Array assignment requires type '%s'"
+						 " but expression is of type '%s'"
+						 "\n\tYou will need to rewrite or cast the expression",
+						 typeidTypeName(typeneeded),
+						 typeidTypeName(typesource));
+			}
+		}
+	}
 
+	/*
+	 * Ready to build the ArrayRef node.
+	 */
 	aref = makeNode(ArrayRef);
 	aref->refattrlength = type_struct_array->typlen;
 	aref->refelemlength = type_struct_element->typlen;
-	aref->refelemtype = type_struct_array->typelem;
+	aref->refelemtype = typeresult; /* XXX should save element type too */
 	aref->refelembyval = type_struct_element->typbyval;
 	aref->refupperindexpr = upperIndexpr;
 	aref->reflowerindexpr = lowerIndexpr;
-	aref->refexpr = (Node *) target_expr;
-	aref->refassgnexpr = (Node *) expr;
-
-	/* accessing a single array element? */
-	if (lowerIndexpr == NIL)
-		reftype = aref->refelemtype;
-
-	/* otherwise, request to set a part of the array, by another array */
-	else
-		reftype = typearray;
-
-	aref->refelemtype = reftype;
+	aref->refexpr = arrayBase;
+	aref->refassgnexpr = assignFrom;
 
 	return aref;
 }
 
 /*
- *
  * make_const -
  *
  * - takes a lispvalue, (as returned to the yacc routine by the lexer)
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index d3662aea8263226945fbfd1411e8db2a604e2555..79b08635f0273ada348dedccc34bfc1d798c0480 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.25 1999/07/17 20:17:25 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.26 1999/07/19 00:26:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,10 +25,8 @@
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 
-static void checkTargetTypes(ParseState *pstate, char *target_colname,
-				 char *refname, char *colname);
 
-struct
+static struct
 {
 	char	   *field;
 	int			code;
@@ -97,7 +95,6 @@ refnameRangeTablePosn(ParseState *pstate, char *refname, int *sublevels_up)
 	int			index;
 	List	   *temp;
 
-
 	if (sublevels_up)
 		*sublevels_up = 0;
 
@@ -175,7 +172,7 @@ colnameRangeTableEntry(ParseState *pstate, char *colname)
 /*
  * put new entry in pstate p_rtable structure, or return pointer
  * if pstate null
-*/
+ */
 RangeTblEntry *
 addRangeTableEntry(ParseState *pstate,
 				   char *relname,
@@ -239,38 +236,31 @@ addRangeTableEntry(ParseState *pstate,
 List *
 expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno)
 {
+	List	   *te_list = NIL;
+	RangeTblEntry *rte;
 	Relation	rel;
-	List	   *te_tail = NIL,
-			   *te_head = NIL;
-	Var		   *varnode;
 	int			varattno,
 				maxattrs;
-	RangeTblEntry *rte;
 
 	rte = refnameRangeTableEntry(pstate, refname);
 	if (rte == NULL)
 		rte = addRangeTableEntry(pstate, relname, refname, FALSE, FALSE);
 
 	rel = heap_open(rte->relid);
-
 	if (rel == NULL)
 		elog(ERROR, "Unable to expand all -- heap_open failed on %s",
 			 rte->refname);
 
 	maxattrs = RelationGetNumberOfAttributes(rel);
 
-	for (varattno = 0; varattno <= maxattrs - 1; varattno++)
+	for (varattno = 0; varattno < maxattrs; varattno++)
 	{
 		char	   *attrname;
-		char	   *resname = NULL;
+		Var		   *varnode;
 		TargetEntry *te = makeNode(TargetEntry);
 
-		attrname = pstrdup((rel->rd_att->attrs[varattno]->attname).data);
-		varnode = (Var *) make_var(pstate, rte->relid, refname, attrname);
-
-		handleTargetColname(pstate, &resname, refname, attrname);
-		if (resname != NULL)
-			attrname = resname;
+		attrname = pstrdup(rel->rd_att->attrs[varattno]->attname.data);
+		varnode = make_var(pstate, rte->relid, refname, attrname);
 
 		/*
 		 * Even if the elements making up a set are complex, the set
@@ -285,15 +275,12 @@ expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno)
 								(Oid) 0,
 								false);
 		te->expr = (Node *) varnode;
-		if (te_head == NIL)
-			te_head = te_tail = lcons(te, NIL);
-		else
-			te_tail = lappend(te_tail, te);
+		te_list = lappend(te_list, te);
 	}
 
 	heap_close(rel);
 
-	return te_head;
+	return te_list;
 }
 
 /*
@@ -378,102 +365,3 @@ attnumTypeId(Relation rd, int attid)
 	 */
 	return rd->rd_att->attrs[attid - 1]->atttypid;
 }
-
-/* handleTargetColname()
- * Use column names from insert.
- */
-void
-handleTargetColname(ParseState *pstate, char **resname,
-					char *refname, char *colname)
-{
-	if (pstate->p_is_insert)
-	{
-		if (pstate->p_insert_columns != NIL)
-		{
-			Ident	   *id = lfirst(pstate->p_insert_columns);
-
-			*resname = id->name;
-			pstate->p_insert_columns = lnext(pstate->p_insert_columns);
-		}
-		else
-			elog(ERROR, "INSERT has more expressions than target columns");
-	}
-	if (pstate->p_is_insert || pstate->p_is_update)
-		checkTargetTypes(pstate, *resname, refname, colname);
-}
-
-/* checkTargetTypes()
- * Checks value and target column types.
- */
-static void
-checkTargetTypes(ParseState *pstate, char *target_colname,
-				 char *refname, char *colname)
-{
-	Oid			attrtype_id,
-				attrtype_target;
-	int			resdomno_id,
-				resdomno_target;
-	RangeTblEntry *rte;
-
-	if (target_colname == NULL || colname == NULL)
-		return;
-
-	if (refname != NULL)
-		rte = refnameRangeTableEntry(pstate, refname);
-	else
-	{
-		rte = colnameRangeTableEntry(pstate, colname);
-		if (rte == (RangeTblEntry *) NULL)
-			elog(ERROR, "Attribute %s not found", colname);
-		refname = rte->refname;
-	}
-
-/*
-	if (pstate->p_is_insert && rte == pstate->p_target_rangetblentry)
-		elog(ERROR, "'%s' not available in this context", colname);
-*/
-	resdomno_id = get_attnum(rte->relid, colname);
-	attrtype_id = get_atttype(rte->relid, resdomno_id);
-
-	resdomno_target = attnameAttNum(pstate->p_target_relation, target_colname);
-	attrtype_target = attnumTypeId(pstate->p_target_relation, resdomno_target);
-
-#ifdef NOT_USED
-	if ((attrtype_id != attrtype_target)
-		|| (get_atttypmod(rte->relid, resdomno_id) !=
-	   get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target)))
-	{
-		if (can_coerce_type(1, &attrtype_id, &attrtype_target))
-		{
-			Node	   *expr = coerce_type(pstate, expr, attrtype_id,
-										   attrtype_target,
-										   get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target));
-
-			elog(ERROR, "Type %s(%d) can be coerced to match target column %s(%d)",
-				 colname, get_atttypmod(rte->relid, resdomno_id),
-				 target_colname, get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target));
-		}
-		else
-		{
-			elog(ERROR, "Type or size of %s(%d) does not match target column %s(%d)",
-				 colname, get_atttypmod(rte->relid, resdomno_id),
-				 target_colname, get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target));
-		}
-	}
-#else
-	if (attrtype_id != attrtype_target)
-		elog(ERROR, "Type of '%s' does not match target column '%s'",
-			 colname, target_colname);
-
-	if (attrtype_id == BPCHAROID &&
-		get_atttypmod(rte->relid, resdomno_id) !=
-		get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target))
-		elog(ERROR, "Length of '%s' is not equal to the length of target column '%s'",
-			 colname, target_colname);
-	if (attrtype_id == VARCHAROID &&
-		get_atttypmod(rte->relid, resdomno_id) >
-		get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target))
-		elog(ERROR, "Length of '%s' is longer than length of target column '%s'",
-			 colname, target_colname);
-#endif
-}
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index 35d064b09808d5c44a8a26afb7a00fb7a9c2c053..48973f67d9b928a7532ab2688d4dd45076e661b4 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.45 1999/07/17 20:17:26 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.46 1999/07/19 00:26:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,636 +24,244 @@
 #include "utils/syscache.h"
 
 
+static Node *SizeTargetExpr(ParseState *pstate, Node *expr,
+							Oid attrtype, int32 attrtypmod);
 static List *ExpandAllTables(ParseState *pstate);
 static char *FigureColname(Node *expr, Node *resval);
-static Node *SizeTargetExpr(ParseState *pstate,
-			   Node *expr,
-			   Oid attrtype,
-			   int32 attrtypmod);
 
 
-/* MakeTargetEntryIdent()
- * Transforms an Ident Node to a Target Entry
- * Created this function to allow the ORDER/GROUP BY clause to be able
- *	to construct a TargetEntry from an Ident.
- *
- * resjunk = TRUE will hide the target entry in the final result tuple.
- *		daveh@insightdist.com	  5/20/98
+/*
+ * transformTargetEntry()
+ *	Transform any ordinary "expression-type" node into a targetlist entry.
+ *	This is exported so that parse_clause.c can generate targetlist entries
+ *	for ORDER/GROUP BY items that are not already in the targetlist.
  *
- * Added more conversion logic to match up types from source to target.
- * - thomas 1998-06-02
+ * node		the (untransformed) parse tree for the value expression.
+ * expr		the transformed expression, or NULL if caller didn't do it yet.
+ * colname	the column name to be assigned, or NULL if none yet set.
+ * resjunk	true if the target should be marked resjunk, ie, it is not
+ *			wanted in the final projected tuple.
  */
 TargetEntry *
-MakeTargetEntryIdent(ParseState *pstate,
+transformTargetEntry(ParseState *pstate,
 					 Node *node,
-					 char **resname,
-					 char *refname,
+					 Node *expr,
 					 char *colname,
 					 bool resjunk)
 {
-	Node	   *expr = NULL;
-	Oid			attrtype_target;
-	TargetEntry *tent = makeNode(TargetEntry);
-
-	if (pstate->p_is_insert && !resjunk)
-	{
-
-		/*
-		 * Assign column name of destination column to the new TLE. XXX
-		 * this is probably WRONG in INSERT ... SELECT case, since
-		 * handling of GROUP BY and so forth probably should use the
-		 * source table's names not the destination's names.
-		 */
-		if (pstate->p_insert_columns != NIL)
-		{
-			Ident	   *id = lfirst(pstate->p_insert_columns);
-
-			*resname = id->name;
-			pstate->p_insert_columns = lnext(pstate->p_insert_columns);
-		}
-		else
-			elog(ERROR, "INSERT has more expressions than target columns");
-	}
-
-	if ((pstate->p_is_insert || pstate->p_is_update) && !resjunk)
-	{
-		Oid			attrtype_id;
-		int			resdomno_id,
-					resdomno_target;
-		RangeTblEntry *rte;
-		char	   *target_colname;
-		int32		attrtypmod,
-					attrtypmod_target;
-
-		target_colname = *resname;
-
-		/*
-		 * this looks strange to me, returning an empty TargetEntry bjm
-		 * 1998/08/24
-		 */
-		if (target_colname == NULL || colname == NULL)
-			return tent;
-
-		if (refname != NULL)
-			rte = refnameRangeTableEntry(pstate, refname);
-		else
-		{
-			rte = colnameRangeTableEntry(pstate, colname);
-			if (rte == (RangeTblEntry *) NULL)
-				elog(ERROR, "Attribute %s not found", colname);
-			refname = rte->refname;
-		}
-
-		resdomno_id = get_attnum(rte->relid, colname);
-		attrtype_id = get_atttype(rte->relid, resdomno_id);
-		attrtypmod = get_atttypmod(rte->relid, resdomno_id);
-
-		resdomno_target = attnameAttNum(pstate->p_target_relation, target_colname);
-		attrtype_target = attnumTypeId(pstate->p_target_relation, resdomno_target);
-		attrtypmod_target = get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target);
-
-		if ((attrtype_id != attrtype_target)
-			|| ((attrtypmod_target >= 0) && (attrtypmod_target != attrtypmod)))
-		{
-			if (can_coerce_type(1, &attrtype_id, &attrtype_target))
-			{
-				expr = coerce_type(pstate, node, attrtype_id,
-								   attrtype_target,
-								   get_atttypmod(pstate->p_target_relation->rd_id, resdomno_target));
-				expr = transformExpr(pstate, expr, EXPR_COLUMN_FIRST);
-				tent = MakeTargetEntryExpr(pstate, *resname, expr, false, false);
-				expr = tent->expr;
-			}
-			else
-			{
-				elog(ERROR, "Unable to convert %s to %s for column %s",
-					 typeidTypeName(attrtype_id), typeidTypeName(attrtype_target),
-					 target_colname);
-			}
-		}
-	}
-
-	/*
-	 * here we want to look for column names only, not relation names
-	 * (even though they can be stored in Ident nodes, too)
-	 */
-	if (expr == NULL)
-	{
-		char	   *name;
-		int32		type_mod;
-
-		name = ((*resname != NULL) ? *resname : colname);
-
-		expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
-
-		attrtype_target = exprType(expr);
-		if (nodeTag(expr) == T_Var)
-			type_mod = ((Var *) expr)->vartypmod;
-		else
-			type_mod = -1;
-
-		tent->resdom = makeResdom((AttrNumber) pstate->p_last_resno++,
-								  (Oid) attrtype_target,
-								  type_mod,
-								  name,
-								  (Index) 0,
-								  (Oid) 0,
-								  resjunk);
-		tent->expr = expr;
-	}
-
-	return tent;
-}	/* MakeTargetEntryIdent() */
-
-
-/* MakeTargetEntryExpr()
- * Make a TargetEntry from an expression.
- * arrayRef is a list of transformed A_Indices.
- *
- * For type mismatches between expressions and targets, use the same
- *	techniques as for function and operator type coersion.
- * - thomas 1998-05-08
- *
- * Added resjunk flag and made extern so that it can be use by GROUP/
- * ORDER BY a function or expression not in the target_list
- * -  daveh@insightdist.com 1998-07-31
- */
-TargetEntry *
-MakeTargetEntryExpr(ParseState *pstate,
-					char *colname,
-					Node *expr,
-					List *arrayRef,
-					bool resjunk)
-{
-	Oid			type_id,
-				attrtype;
-	int32		type_mod,
-				attrtypmod;
-	int			resdomno;
-	Relation	rd;
-	bool		attrisset;
+	Oid			type_id;
+	int32		type_mod;
 	Resdom	   *resnode;
 
+	/* Transform the node if caller didn't do it already */
 	if (expr == NULL)
-		elog(ERROR, "Invalid use of NULL expression (internal error)");
+		expr = transformExpr(pstate, node, EXPR_COLUMN_FIRST);
 
 	type_id = exprType(expr);
-	if (nodeTag(expr) == T_Var)
-		type_mod = ((Var *) expr)->vartypmod;
-	else
-		type_mod = -1;
+	type_mod = exprTypmod(expr);
 
-	/* Process target columns that will be receiving results */
-	if ((pstate->p_is_insert || pstate->p_is_update) && !resjunk)
+	if (colname == NULL)
 	{
-
-		/*
-		 * insert or update query -- insert, update work only on one
-		 * relation, so multiple occurence of same resdomno is bogus
+		/* Generate a suitable column name for a column without any
+		 * explicit 'AS ColumnName' clause.
 		 */
-		rd = pstate->p_target_relation;
-		Assert(rd != NULL);
-		resdomno = attnameAttNum(rd, colname);
-		if (resdomno <= 0)
-			elog(ERROR, "Cannot assign to system attribute '%s'", colname);
-		attrisset = attnameIsSet(rd, colname);
-		attrtype = attnumTypeId(rd, resdomno);
-		if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL))
-			attrtype = GetArrayElementType(attrtype);
-		attrtypmod = rd->rd_att->attrs[resdomno - 1]->atttypmod;
-
-		/*
-		 * Check for InvalidOid since that seems to indicate a NULL
-		 * constant...
-		 */
-		if (type_id != InvalidOid)
-		{
-			/* Mismatch on types? then try to coerce to target...  */
-			if (attrtype != type_id)
-			{
-				Oid			typelem;
-
-				if (arrayRef && !(((A_Indices *) lfirst(arrayRef))->lidx))
-					typelem = typeTypElem(typeidType(attrtype));
-				else
-					typelem = attrtype;
-
-				expr = CoerceTargetExpr(pstate, expr, type_id, typelem);
-
-				if (!HeapTupleIsValid(expr))
-					elog(ERROR, "Attribute '%s' is of type '%s'"
-						 " but expression is of type '%s'"
-					"\n\tYou will need to rewrite or cast the expression",
-						 colname,
-						 typeidTypeName(attrtype),
-						 typeidTypeName(type_id));
-			}
-
-			/*
-			 * Apparently going to a fixed-length string? Then explicitly
-			 * size for storage...
-			 */
-			if (attrtypmod > 0)
-				expr = SizeTargetExpr(pstate, expr, attrtype, attrtypmod);
-		}
-
-		if (arrayRef != NIL)
-		{
-			Expr	   *target_expr;
-			Attr	   *att = makeNode(Attr);
-			List	   *ar = arrayRef;
-			List	   *upperIndexpr = NIL;
-			List	   *lowerIndexpr = NIL;
-
-			att->relname = pstrdup(RelationGetRelationName(rd)->data);
-			att->attrs = lcons(makeString(colname), NIL);
-			target_expr = (Expr *) ParseNestedFuncOrColumn(pstate, att,
-												   &pstate->p_last_resno,
-													  EXPR_COLUMN_FIRST);
-			while (ar != NIL)
-			{
-				A_Indices  *ind = lfirst(ar);
-
-				if (lowerIndexpr || (!upperIndexpr && ind->lidx))
-				{
-
-					/*
-					 * XXX assume all lowerIndexpr is non-null in this
-					 * case
-					 */
-					lowerIndexpr = lappend(lowerIndexpr, ind->lidx);
-				}
-				upperIndexpr = lappend(upperIndexpr, ind->uidx);
-				ar = lnext(ar);
-			}
-
-			expr = (Node *) make_array_set(target_expr,
-										   upperIndexpr,
-										   lowerIndexpr,
-										   (Expr *) expr);
-			attrtype = attnumTypeId(rd, resdomno);
-			attrtypmod = get_atttypmod(RelationGetRelid(rd), resdomno);
-		}
-	}
-	else
-	{
-		resdomno = pstate->p_last_resno++;
-		attrtype = type_id;
-		attrtypmod = type_mod;
+		colname = FigureColname(expr, node);
 	}
 
-	resnode = makeResdom((AttrNumber) resdomno,
-						 (Oid) attrtype,
-						 attrtypmod,
+	resnode = makeResdom((AttrNumber) pstate->p_last_resno++,
+						 type_id,
+						 type_mod,
 						 colname,
 						 (Index) 0,
-						 (Oid) 0,
+						 (Oid) InvalidOid,
 						 resjunk);
 
 	return makeTargetEntry(resnode, expr);
-}	/* MakeTargetEntryExpr() */
-
-/*
- *	MakeTargetEntryCase()
- *	Make a TargetEntry from a case node.
- */
-static TargetEntry *
-MakeTargetEntryCase(ParseState *pstate,
-					ResTarget *res)
-{
-	TargetEntry *tent;
-	CaseExpr   *expr;
-	Resdom	   *resnode;
-	int			resdomno;
-	Oid			type_id;
-	int32		type_mod;
-
-	expr = (CaseExpr *) transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST);
-
-	type_id = expr->casetype;
-	type_mod = -1;
-	handleTargetColname(pstate, &res->name, NULL, NULL);
-	if (res->name == NULL)
-		res->name = FigureColname((Node *) expr, res->val);
-
-	resdomno = pstate->p_last_resno++;
-	resnode = makeResdom((AttrNumber) resdomno,
-						 (Oid) type_id,
-						 type_mod,
-						 res->name,
-						 (Index) 0,
-						 (Oid) 0,
-						 false);
-
-	tent = makeNode(TargetEntry);
-	tent->resdom = resnode;
-	tent->expr = (Node *) expr;
+}
 
-	return tent;
-}	/* MakeTargetEntryCase() */
 
 /*
- *	MakeTargetEntryComplex()
- *	Make a TargetEntry from a complex node.
+ * transformTargetList()
+ * Turns a list of ResTarget's into a list of TargetEntry's.
+ *
+ * At this point, we don't care whether we are doing SELECT, INSERT,
+ * or UPDATE; we just transform the given expressions.
  */
-static TargetEntry *
-MakeTargetEntryComplex(ParseState *pstate,
-					   ResTarget *res)
+List *
+transformTargetList(ParseState *pstate, List *targetlist)
 {
-	Node	   *expr = transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST);
+	List	   *p_target = NIL;
 
-	handleTargetColname(pstate, &res->name, NULL, NULL);
-	/* note indirection has not been transformed */
-	if (pstate->p_is_insert && res->indirection != NIL)
+	while (targetlist != NIL)
 	{
-		/* this is an array assignment */
-		char	   *val;
-		char	   *str,
-				   *save_str;
-		List	   *elt;
-		int			i = 0,
-					ndims;
-		int			lindx[MAXDIM],
-					uindx[MAXDIM];
-		int			resdomno;
-		Relation	rd;
-		Value	   *constval;
-
-		if (exprType(expr) != UNKNOWNOID || !IsA(expr, Const))
-			elog(ERROR, "String constant expected (internal error)");
-
-		val = (char *) textout((struct varlena *)
-							   ((Const *) expr)->constvalue);
-		str = save_str = (char *) palloc(strlen(val) + MAXDIM * 25 + 2);
-		foreach(elt, res->indirection)
-		{
-			A_Indices  *aind = (A_Indices *) lfirst(elt);
+		ResTarget  *res = (ResTarget *) lfirst(targetlist);
 
-			aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST);
-			if (!IsA(aind->uidx, Const))
-				elog(ERROR, "Array Index for Append should be a constant");
+		if (IsA(res->val, Attr))
+		{
+			Attr	   *att = (Attr *) res->val;
 
-			uindx[i] = ((Const *) aind->uidx)->constvalue;
-			if (aind->lidx != NULL)
+			if (att->relname != NULL && strcmp(att->relname, "*") == 0)
 			{
-				aind->lidx = transformExpr(pstate, aind->lidx, EXPR_COLUMN_FIRST);
-				if (!IsA(aind->lidx, Const))
-					elog(ERROR, "Array Index for Append should be a constant");
-
-				lindx[i] = ((Const *) aind->lidx)->constvalue;
+				/*
+				 * Target item is a single '*', expand all tables
+				 * (eg. SELECT * FROM emp)
+				 */
+				p_target = nconc(p_target,
+								 ExpandAllTables(pstate));
+			}
+			else if (att->attrs != NIL &&
+					 strcmp(strVal(lfirst(att->attrs)), "*") == 0)
+			{
+				/*
+				 * Target item is relation.*, expand that table
+				 * (eg. SELECT emp.*, dname FROM emp, dept)
+				 */
+				p_target = nconc(p_target,
+								 expandAll(pstate,
+										   att->relname,
+										   att->relname,
+										   &pstate->p_last_resno));
 			}
 			else
-				lindx[i] = 1;
-			if (lindx[i] > uindx[i])
-				elog(ERROR, "Lower index cannot be greater than upper index");
-
-			sprintf(str, "[%d:%d]", lindx[i], uindx[i]);
-			str += strlen(str);
-			i++;
+			{
+				/* Plain Attr node, treat it as an expression */
+				p_target = lappend(p_target,
+								   transformTargetEntry(pstate,
+														res->val,
+														NULL,
+														res->name,
+														false));
+			}
 		}
-		sprintf(str, "=%s", val);
-		rd = pstate->p_target_relation;
-		Assert(rd != NULL);
-		resdomno = attnameAttNum(rd, res->name);
-		ndims = attnumAttNelems(rd, resdomno);
-		if (i != ndims)
-			elog(ERROR, "Array dimensions do not match");
-
-		constval = makeNode(Value);
-		constval->type = T_String;
-		constval->val.str = save_str;
-		return MakeTargetEntryExpr(pstate, res->name,
-								   (Node *) make_const(constval),
-								   NULL, false);
-		pfree(save_str);
-	}
-	else
-	{
-		/* this is not an array assignment */
-		char	   *colname = res->name;
-
-		if (colname == NULL)
+		else
 		{
-
-			/*
-			 * if you're wondering why this is here, look at the yacc
-			 * grammar for why a name can be missing. -ay
-			 */
-			colname = FigureColname(expr, res->val);
+			/* Everything else but Attr */
+			p_target = lappend(p_target,
+							   transformTargetEntry(pstate,
+													res->val,
+													NULL,
+													res->name,
+													false));
 		}
-		if (res->indirection)
-		{
-			List	   *ilist = res->indirection;
-
-			while (ilist != NIL)
-			{
-				A_Indices  *ind = lfirst(ilist);
 
-				ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST);
-				ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST);
-				ilist = lnext(ilist);
-			}
-		}
-		res->name = colname;
-		return MakeTargetEntryExpr(pstate, res->name, expr,
-								   res->indirection, false);
+		targetlist = lnext(targetlist);
 	}
+
+	return p_target;
 }
 
+
 /*
- *	MakeTargetEntryAttr()
- *	Make a TargetEntry from a complex node.
+ * updateTargetListEntry()
+ *	This is used in INSERT and UPDATE statements only.  It prepares a
+ *	TargetEntry for assignment to a column of the target table.
+ *	This includes coercing the given value to the target column's type
+ *	(if necessary), and dealing with any subscripts attached to the target
+ *	column itself.
+ *
+ * pstate		parse state
+ * tle			target list entry to be modified
+ * colname		target column name (ie, name of attribute to be assigned to)
+ * indirection	subscripts for target column, if any
  */
-static TargetEntry *
-MakeTargetEntryAttr(ParseState *pstate,
-					ResTarget *res)
+void
+updateTargetListEntry(ParseState *pstate,
+					  TargetEntry *tle,
+					  char *colname,
+					  List *indirection)
 {
-	Oid			type_id;
-	int32		type_mod;
-	Attr	   *att = (Attr *) res->val;
-	Node	   *result;
-	char	   *attrname;
-	char	   *resname;
-	Resdom	   *resnode;
+	Oid			type_id = exprType(tle->expr); /* type of value provided */
+	Oid			attrtype;		/* type of target column */
+	int32		attrtypmod;
+	Resdom	   *resnode = tle->resdom;
+	Relation	rd = pstate->p_target_relation;
 	int			resdomno;
-	List	   *attrs = att->attrs;
-	TargetEntry *tent;
 
-	attrname = strVal(lfirst(att->attrs));
+	Assert(rd != NULL);
+	resdomno = attnameAttNum(rd, colname);
+	if (resdomno <= 0)
+		elog(ERROR, "Cannot assign to system attribute '%s'", colname);
+	attrtype = attnumTypeId(rd, resdomno);
+	attrtypmod = rd->rd_att->attrs[resdomno - 1]->atttypmod;
 
 	/*
-	 * Target item is fully specified: ie. relation.attribute
+	 * If there are subscripts on the target column, prepare an
+	 * array assignment expression.  This will generate an array value
+	 * that the source value has been inserted into, which can then
+	 * be placed in the new tuple constructed by INSERT or UPDATE.
+	 * Note that transformArraySubscripts takes care of type coercion.
 	 */
-	result = ParseNestedFuncOrColumn(pstate, att, &pstate->p_last_resno, EXPR_COLUMN_FIRST);
-	handleTargetColname(pstate, &res->name, att->relname, attrname);
-	if (att->indirection != NIL)
+	if (indirection)
 	{
-		List	   *ilist = att->indirection;
-
-		while (ilist != NIL)
+		Attr	   *att = makeNode(Attr);
+		Node	   *arrayBase;
+		ArrayRef   *aref;
+
+		att->relname = pstrdup(RelationGetRelationName(rd)->data);
+		att->attrs = lcons(makeString(colname), NIL);
+		arrayBase = ParseNestedFuncOrColumn(pstate, att,
+											&pstate->p_last_resno,
+											EXPR_COLUMN_FIRST);
+		aref = transformArraySubscripts(pstate, arrayBase,
+										indirection,
+										pstate->p_is_insert,
+										tle->expr);
+		if (pstate->p_is_insert)
 		{
-			A_Indices  *ind = lfirst(ilist);
-
-			ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST);
-			ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST);
-			ilist = lnext(ilist);
+			/*
+			 * The command is INSERT INTO table (arraycol[subscripts]) ...
+			 * so there is not really a source array value to work with.
+			 * Let the executor do something reasonable, if it can.
+			 * Notice that we forced transformArraySubscripts to treat
+			 * the subscripting op as an array-slice op above, so the
+			 * source data will have been coerced to array type.
+			 */
+			aref->refexpr = NULL; /* signal there is no source array */
 		}
-		result = (Node *) make_array_ref(result, att->indirection);
+		tle->expr = (Node *) aref;
 	}
-	type_id = exprType(result);
-	if (nodeTag(result) == T_Var)
-		type_mod = ((Var *) result)->vartypmod;
 	else
-		type_mod = -1;
-	/* move to last entry */
-	while (lnext(attrs) != NIL)
-		attrs = lnext(attrs);
-	resname = (res->name) ? res->name : strVal(lfirst(attrs));
-	if (pstate->p_is_insert || pstate->p_is_update)
 	{
-		Relation	rd;
-
 		/*
-		 * insert or update query -- insert, update work only on one
-		 * relation, so multiple occurence of same resdomno is bogus
+		 * For normal non-subscripted target column, do type checking
+		 * and coercion.  But accept InvalidOid, which indicates the
+		 * source is a NULL constant.
 		 */
-		rd = pstate->p_target_relation;
-		Assert(rd != NULL);
-		resdomno = attnameAttNum(rd, res->name);
-	}
-	else
-		resdomno = pstate->p_last_resno++;
-	resnode = makeResdom((AttrNumber) resdomno,
-						 (Oid) type_id,
-						 type_mod,
-						 resname,
-						 (Index) 0,
-						 (Oid) 0,
-						 false);
-	tent = makeNode(TargetEntry);
-	tent->resdom = resnode;
-	tent->expr = result;
-	return tent;
-}
-
-
-/* transformTargetList()
- * Turns a list of ResTarget's into a list of TargetEntry's.
- */
-List *
-transformTargetList(ParseState *pstate, List *targetlist)
-{
-	List	   *p_target = NIL;
-	List	   *tail_p_target = NIL;
-
-	while (targetlist != NIL)
-	{
-		ResTarget  *res = (ResTarget *) lfirst(targetlist);
-		TargetEntry *tent = NULL;
-
-		switch (nodeTag(res->val))
-		{
-			case T_Ident:
-				{
-					char	   *identname;
-
-					identname = ((Ident *) res->val)->name;
-					tent = MakeTargetEntryIdent(pstate,
-												(Node *) res->val, &res->name, NULL, identname, false);
-					break;
-				}
-			case T_ParamNo:
-			case T_FuncCall:
-			case T_A_Const:
-			case T_A_Expr:
-				{
-					tent = MakeTargetEntryComplex(pstate, res);
-					break;
-				}
-			case T_CaseExpr:
-				{
-					tent = MakeTargetEntryCase(pstate, res);
-					break;
-				}
-			case T_Attr:
-				{
-					bool		expand_star = false;
-					char	   *attrname;
-					Attr	   *att = (Attr *) res->val;
-
-					/*
-					 * Target item is a single '*', expand all tables (eg.
-					 * SELECT * FROM emp)
-					 */
-					if (att->relname != NULL && !strcmp(att->relname, "*"))
-					{
-						if (tail_p_target == NIL)
-							p_target = tail_p_target = ExpandAllTables(pstate);
-						else
-							lnext(tail_p_target) = ExpandAllTables(pstate);
-						expand_star = true;
-					}
-					else
-					{
-
-						/*
-						 * Target item is relation.*, expand the table
-						 * (eg. SELECT emp.*, dname FROM emp, dept)
-						 */
-						attrname = strVal(lfirst(att->attrs));
-						if (att->attrs != NIL && !strcmp(attrname, "*"))
-						{
-
-							/*
-							 * tail_p_target is the target list we're
-							 * building in the while loop. Make sure we
-							 * fix it after appending more nodes.
-							 */
-							if (tail_p_target == NIL)
-								p_target = tail_p_target = expandAll(pstate, att->relname,
-									att->relname, &pstate->p_last_resno);
-							else
-								lnext(tail_p_target) = expandAll(pstate, att->relname, att->relname,
-												  &pstate->p_last_resno);
-							expand_star = true;
-						}
-					}
-					if (expand_star)
-					{
-						while (lnext(tail_p_target) != NIL)
-							/* make sure we point to the last target entry */
-							tail_p_target = lnext(tail_p_target);
-
-						/*
-						 * skip rest of while loop
-						 */
-						targetlist = lnext(targetlist);
-						continue;
-					}
-					else
-					{
-						tent = MakeTargetEntryAttr(pstate, res);
-						break;
-					}
-				}
-			default:
-				/* internal error */
-				elog(ERROR, "Unable to transform targetlist (internal error)");
-				break;
-		}
-
-		if (p_target == NIL)
-			p_target = tail_p_target = lcons(tent, NIL);
-		else
+		if (type_id != InvalidOid)
 		{
-			lnext(tail_p_target) = lcons(tent, NIL);
-			tail_p_target = lnext(tail_p_target);
+			if (type_id != attrtype)
+			{
+				tle->expr = CoerceTargetExpr(pstate, tle->expr,
+											 type_id, attrtype);
+				if (tle->expr == NULL)
+					elog(ERROR, "Attribute '%s' is of type '%s'"
+						 " but expression is of type '%s'"
+						 "\n\tYou will need to rewrite or cast the expression",
+						 colname,
+						 typeidTypeName(attrtype),
+						 typeidTypeName(type_id));
+			}
+			/*
+			 * If the target is a fixed-length type, it may need a length
+			 * coercion as well as a type coercion.
+			 */
+			if (attrtypmod > 0 &&
+				attrtypmod != exprTypmod(tle->expr))
+				tle->expr = SizeTargetExpr(pstate, tle->expr,
+										   attrtype, attrtypmod);
 		}
-		targetlist = lnext(targetlist);
 	}
 
-	return p_target;
-}	/* transformTargetList() */
+	/*
+	 * The result of the target expression should now match the destination
+	 * column's type.  Also, reset the resname and resno to identify
+	 * the destination column --- rewriter and planner depend on that!
+	 */
+	resnode->restype = attrtype;
+	resnode->restypmod = attrtypmod;
+	resnode->resname = colname;
+	resnode->resno = (AttrNumber) resdomno;
+}
 
 
 Node *
@@ -689,12 +297,19 @@ CoerceTargetExpr(ParseState *pstate,
 		expr = NULL;
 
 	return expr;
-}	/* CoerceTargetExpr() */
+}
 
 
-/* SizeTargetExpr()
- * Apparently going to a fixed-length string?
- * Then explicitly size for storage...
+/*
+ * SizeTargetExpr()
+ *
+ * If the target column type possesses a function named for the type
+ * and having parameter signature (columntype, int4), we assume that
+ * the type requires coercion to its own length and that the said
+ * function should be invoked to do that.
+ *
+ * Currently, "bpchar" (ie, char(N)) is the only such type, but try
+ * to be more general than a hard-wired test...
  */
 static Node *
 SizeTargetExpr(ParseState *pstate,
@@ -702,13 +317,10 @@ SizeTargetExpr(ParseState *pstate,
 			   Oid attrtype,
 			   int32 attrtypmod)
 {
-	int			i;
-	HeapTuple	ftup;
 	char	   *funcname;
 	Oid			oid_array[MAXFARGS];
-
-	FuncCall   *func;
-	A_Const    *cons;
+	HeapTuple	ftup;
+	int			i;
 
 	funcname = typeidTypeName(attrtype);
 	oid_array[0] = attrtype;
@@ -725,6 +337,9 @@ SizeTargetExpr(ParseState *pstate,
 
 	if (HeapTupleIsValid(ftup))
 	{
+		FuncCall   *func;
+		A_Const    *cons;
+
 		func = makeNode(FuncCall);
 		func->funcname = funcname;
 
@@ -737,29 +352,27 @@ SizeTargetExpr(ParseState *pstate,
 	}
 
 	return expr;
-}	/* SizeTargetExpr() */
+}
 
 
 /*
  * makeTargetNames -
  *	  generate a list of column names if not supplied or
- *	  test supplied column names to make sure they are in target table
+ *	  test supplied column names to make sure they are in target table.
  *	  (used exclusively for inserts)
  */
 List *
 makeTargetNames(ParseState *pstate, List *cols)
 {
-	List	   *tl = NULL;
-
-	/* Generate ResTarget if not supplied */
-
 	if (cols == NIL)
 	{
-		int			numcol;
-		int			i;
+		/*
+		 * Generate default column list for INSERT.
+		 */
 		Form_pg_attribute *attr = pstate->p_target_relation->rd_att->attrs;
+		int			numcol = pstate->p_target_relation->rd_rel->relnatts;
+		int			i;
 
-		numcol = pstate->p_target_relation->rd_rel->relnatts;
 		for (i = 0; i < numcol; i++)
 		{
 			Ident	   *id = makeNode(Ident);
@@ -768,27 +381,30 @@ makeTargetNames(ParseState *pstate, List *cols)
 			StrNCpy(id->name, attr[i]->attname.data, NAMEDATALEN);
 			id->indirection = NIL;
 			id->isRel = false;
-			if (tl == NIL)
-				cols = tl = lcons(id, NIL);
-			else
-			{
-				lnext(tl) = lcons(id, NIL);
-				tl = lnext(tl);
-			}
+			cols = lappend(cols, id);
 		}
 	}
 	else
 	{
+		/*
+		 * Do initial validation of user-supplied INSERT column list.
+		 */
+		List	   *tl;
+
 		foreach(tl, cols)
 		{
-			List	   *nxt;
 			char	   *name = ((Ident *) lfirst(tl))->name;
+			List	   *nxt;
 
-			/* elog on failure */
+			/* Lookup column name, elog on failure */
 			attnameAttNum(pstate->p_target_relation, name);
+			/* Check for duplicates */
 			foreach(nxt, lnext(tl))
-				if (!strcmp(name, ((Ident *) lfirst(nxt))->name))
-				elog(ERROR, "Attribute '%s' should be specified only once", name);
+			{
+				if (strcmp(name, ((Ident *) lfirst(nxt))->name) == 0)
+					elog(ERROR, "Attribute '%s' specified more than once",
+						 name);
+			}
 		}
 	}
 
@@ -804,57 +420,37 @@ static List *
 ExpandAllTables(ParseState *pstate)
 {
 	List	   *target = NIL;
-	List	   *legit_rtable = NIL;
 	List	   *rt,
 			   *rtable;
 
 	rtable = pstate->p_rtable;
 	if (pstate->p_is_rule)
 	{
-
 		/*
 		 * skip first two entries, "*new*" and "*current*"
 		 */
-		rtable = lnext(lnext(pstate->p_rtable));
+		rtable = lnext(lnext(rtable));
 	}
 
 	/* SELECT *; */
-	if (rtable == NULL)
+	if (rtable == NIL)
 		elog(ERROR, "Wildcard with no tables specified.");
 
-	/*
-	 * go through the range table and make a list of range table entries
-	 * which we will expand.
-	 */
 	foreach(rt, rtable)
 	{
 		RangeTblEntry *rte = lfirst(rt);
 
 		/*
-		 * we only expand those specify in the from clause. (This will
+		 * we only expand those listed in the from clause. (This will
 		 * also prevent us from using the wrong table in inserts: eg.
 		 * tenk2 in "insert into tenk2 select * from tenk1;")
 		 */
 		if (!rte->inFromCl)
 			continue;
-		legit_rtable = lappend(legit_rtable, rte);
-	}
-
-	foreach(rt, legit_rtable)
-	{
-		RangeTblEntry *rte = lfirst(rt);
-		List	   *temp = target;
 
-		if (temp == NIL)
-			target = expandAll(pstate, rte->relname, rte->refname,
-							   &pstate->p_last_resno);
-		else
-		{
-			while (temp != NIL && lnext(temp) != NIL)
-				temp = lnext(temp);
-			lnext(temp) = expandAll(pstate, rte->relname, rte->refname,
-									&pstate->p_last_resno);
-		}
+		target = nconc(target,
+					   expandAll(pstate, rte->relname, rte->refname,
+								 &pstate->p_last_resno));
 	}
 	return target;
 }
@@ -862,29 +458,47 @@ ExpandAllTables(ParseState *pstate)
 /*
  * FigureColname -
  *	  if the name of the resulting column is not specified in the target
- *	  list, we have to guess.
+ *	  list, we have to guess a suitable name.  The SQL spec provides some
+ *	  guidance, but not much...
  *
  */
 static char *
 FigureColname(Node *expr, Node *resval)
 {
-	switch (nodeTag(expr))
+	/* Some of these are easiest to do with the untransformed node */
+	switch (nodeTag(resval))
 	{
-			case T_Aggref:
-			return (char *) ((Aggref *) expr)->aggname;
-		case T_Expr:
-			if (((Expr *) expr)->opType == FUNC_EXPR)
+		case T_Ident:
+			return ((Ident *) resval)->name;
+		case T_Attr:
 			{
-				if (nodeTag(resval) == T_FuncCall)
-					return ((FuncCall *) resval)->funcname;
+				List	   *attrs = ((Attr *) resval)->attrs;
+				if (attrs)
+				{
+					while (lnext(attrs) != NIL)
+						attrs = lnext(attrs);
+					return strVal(lfirst(attrs));
+				}
 			}
 			break;
+		default:
+			break;
+	}
+	/* Otherwise, work with the transformed node */
+	switch (nodeTag(expr))
+	{
+		case T_Expr:
+			if (((Expr *) expr)->opType == FUNC_EXPR && IsA(resval, FuncCall))
+				return ((FuncCall *) resval)->funcname;
+			break;
+		case T_Aggref:
+			return ((Aggref *) expr)->aggname;
 		case T_CaseExpr:
 			{
 				char	   *name;
 
 				name = FigureColname(((CaseExpr *) expr)->defresult, resval);
-				if (!strcmp(name, "?column?"))
+				if (strcmp(name, "?column?") == 0)
 					name = "case";
 				return name;
 			}
diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h
index a50ab4e039146f834ebcfa93b6a4f74045428b39..3b1aafa07d923e709a14b6aaa917d7cb78a7ec47 100644
--- a/src/include/parser/parse_clause.h
+++ b/src/include/parser/parse_clause.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_clause.h,v 1.11 1999/07/15 23:04:01 momjian Exp $
+ * $Id: parse_clause.h,v 1.12 1999/07/19 00:26:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,8 +15,10 @@
 
 #include "parser/parse_node.h"
 
-extern void makeRangeTable(ParseState *pstate, char *relname, List *frmList, Node **qual);
-extern Node *transformWhereClause(ParseState *pstate, Node *where, Node *using);
+extern void makeRangeTable(ParseState *pstate, List *frmList, Node **qual);
+extern void setTargetTable(ParseState *pstate, char *relname);
+extern Node *transformWhereClause(ParseState *pstate, Node *where,
+								  Node *using);
 extern List *transformGroupClause(ParseState *pstate, List *grouplist,
 					 List *targetlist);
 extern List *transformSortClause(ParseState *pstate,
diff --git a/src/include/parser/parse_expr.h b/src/include/parser/parse_expr.h
index cbc4095e330fa3f5401aa2dac18387c88b4c0f4c..3bd4d3fb47c6b53e3b7bb57d23288a61ce7c94a8 100644
--- a/src/include/parser/parse_expr.h
+++ b/src/include/parser/parse_expr.h
@@ -1,12 +1,12 @@
 /*-------------------------------------------------------------------------
  *
- * parse_exer.h
+ * parse_expr.h
  *
  *
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_expr.h,v 1.13 1999/07/15 23:04:02 momjian Exp $
+ * $Id: parse_expr.h,v 1.14 1999/07/19 00:26:16 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,8 +16,12 @@
 #include "parser/parse_node.h"
 #include "parser/parse_type.h"
 
+#define EXPR_COLUMN_FIRST	1
+#define EXPR_RELATION_FIRST 2
+
 extern Node *transformExpr(ParseState *pstate, Node *expr, int precedence);
 extern Oid	exprType(Node *expr);
+extern int32 exprTypmod(Node *expr);
 extern Node *parser_typecast2(Node *expr, Oid exprType, Type tp, int32 attypmod);
 
 #endif	 /* PARSE_EXPR_H */
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 3dd2257db5ef4760db520617d012d6c40e88692d..1591743e0646acf1e2c31e53e12579d1a1b935bd 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_node.h,v 1.14 1999/07/15 23:04:02 momjian Exp $
+ * $Id: parse_node.h,v 1.15 1999/07/19 00:26:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,6 @@ typedef struct ParseState
 {
 	int			p_last_resno;
 	List	   *p_rtable;
-	List	   *p_insert_columns;
 	struct ParseState *parentParseState;
 	bool		p_hasAggs;
 	bool		p_hasSubLinks;
@@ -36,12 +35,11 @@ extern ParseState *make_parsestate(ParseState *parentParseState);
 extern Expr *make_op(char *opname, Node *ltree, Node *rtree);
 extern Var *make_var(ParseState *pstate, Oid relid, char *refname,
 		 char *attrname);
-extern ArrayRef *make_array_ref(Node *expr,
-			   List *indirection);
-extern ArrayRef *make_array_set(Expr *target_expr,
-			   List *upperIndexpr,
-			   List *lowerIndexpr,
-			   Expr *expr);
+extern ArrayRef *transformArraySubscripts(ParseState *pstate,
+										  Node *arrayBase,
+										  List *indirection,
+										  bool forceSlice,
+										  Node *assignFrom);
 extern Const *make_const(Value *value);
 
 #endif	 /* PARSE_NODE_H */
diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h
index e44f89c79baa4531eda67b1dddd57a5aa878ca73..68e5ac7bf1757b08713a03e64925592f51a2489f 100644
--- a/src/include/parser/parse_relation.h
+++ b/src/include/parser/parse_relation.h
@@ -1,17 +1,17 @@
 /*-------------------------------------------------------------------------
  *
- * parse_query.h
- *	  prototypes for parse_query.c.
+ * parse_relation.h
+ *	  prototypes for parse_relation.c.
  *
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_relation.h,v 1.11 1999/07/15 23:04:03 momjian Exp $
+ * $Id: parse_relation.h,v 1.12 1999/07/19 00:26:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-#ifndef PARSE_QUERY_H
-#define PARSE_RANGE_H
+#ifndef PARSE_RELATION_H
+#define PARSE_RELATION_H
 
 #include "parser/parse_node.h"
 
@@ -30,7 +30,5 @@ extern int	attnameAttNum(Relation rd, char *a);
 extern bool attnameIsSet(Relation rd, char *name);
 extern int	attnumAttNelems(Relation rd, int attid);
 extern Oid	attnumTypeId(Relation rd, int attid);
-extern void handleTargetColname(ParseState *pstate, char **resname,
-					char *refname, char *colname);
 
-#endif	 /* PARSE_RANGE_H */
+#endif	 /* PARSE_RELATION_H */
diff --git a/src/include/parser/parse_target.h b/src/include/parser/parse_target.h
index bd520d1bab94fd7c264357e8043dd8f6e0284a0d..c2babecb769d9e1b9b257fbc58420c157132db00 100644
--- a/src/include/parser/parse_target.h
+++ b/src/include/parser/parse_target.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_target.h,v 1.14 1999/07/15 23:04:03 momjian Exp $
+ * $Id: parse_target.h,v 1.15 1999/07/19 00:26:18 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,23 +15,14 @@
 
 #include "parser/parse_node.h"
 
-#define EXPR_COLUMN_FIRST	1
-#define EXPR_RELATION_FIRST 2
-
 extern List *transformTargetList(ParseState *pstate, List *targetlist);
-extern List *makeTargetNames(ParseState *pstate, List *cols);
-extern TargetEntry *MakeTargetEntryIdent(ParseState *pstate,
-					 Node *node,
-					 char **resname,
-					 char *refname,
-					 char *colname,
-					 bool resjunk);
+extern TargetEntry *transformTargetEntry(ParseState *pstate,
+										 Node *node, Node *expr,
+										 char *colname, bool resjunk);
+extern void updateTargetListEntry(ParseState *pstate, TargetEntry *tle,
+								  char *colname, List *indirection);
 extern Node *CoerceTargetExpr(ParseState *pstate, Node *expr,
-				 Oid type_id, Oid attrtype);
-TargetEntry *MakeTargetEntryExpr(ParseState *pstate,
-					char *colname,
-					Node *expr,
-					List *arrayRef,
-					bool resjunk);
+							  Oid type_id, Oid attrtype);
+extern List *makeTargetNames(ParseState *pstate, List *cols);
 
 #endif	 /* PARSE_TARGET_H */