diff --git a/src/backend/access/common/printtup.c b/src/backend/access/common/printtup.c
index 584233c1873f569788fa521708b85decdc04c6e1..27d6ffd508e48000c77b9ac8489c09e36307c70c 100644
--- a/src/backend/access/common/printtup.c
+++ b/src/backend/access/common/printtup.c
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.68 2003/05/05 00:44:55 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/access/common/printtup.c,v 1.69 2003/05/06 00:20:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,7 +23,7 @@
 
 
 static void printtup_setup(DestReceiver *self, int operation,
-			   const char *portalName, TupleDesc typeinfo);
+			   const char *portalName, TupleDesc typeinfo, List *targetlist);
 static void printtup(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
 static void printtup_internal(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self);
 static void printtup_cleanup(DestReceiver *self);
@@ -78,7 +78,7 @@ printtup_create_DR(bool isBinary, bool sendDescrip)
 
 static void
 printtup_setup(DestReceiver *self, int operation,
-			   const char *portalName, TupleDesc typeinfo)
+			   const char *portalName, TupleDesc typeinfo, List *targetlist)
 {
 	DR_printtup *myState = (DR_printtup *) self;
 
@@ -100,7 +100,7 @@ printtup_setup(DestReceiver *self, int operation,
 	 * then we send back the tuple descriptor of the tuples.  
 	 */
 	if (operation == CMD_SELECT && myState->sendDescrip)
-		SendRowDescriptionMessage(typeinfo);
+		SendRowDescriptionMessage(typeinfo, targetlist);
 
 	/* ----------------
 	 * We could set up the derived attr info at this time, but we postpone it
@@ -116,9 +116,15 @@ printtup_setup(DestReceiver *self, int operation,
 
 /*
  * SendRowDescriptionMessage --- send a RowDescription message to the frontend
+ *
+ * Notes: the TupleDesc has typically been manufactured by ExecTypeFromTL()
+ * or some similar function; it does not contain a full set of fields.
+ * The targetlist will be NIL when executing a utility function that does
+ * not have a plan.  If the targetlist isn't NIL then it is a Plan node's
+ * targetlist; it is up to us to ignore resjunk columns in it.
  */
 void
-SendRowDescriptionMessage(TupleDesc typeinfo)
+SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist)
 {
 	Form_pg_attribute *attrs = typeinfo->attrs;
 	int			natts = typeinfo->natts;
@@ -135,9 +141,24 @@ SendRowDescriptionMessage(TupleDesc typeinfo)
 		/* column ID info appears in protocol 3.0 and up */
 		if (proto >= 3)
 		{
-			/* XXX not yet implemented, send zeroes */
-			pq_sendint(&buf, 0, 4);
-			pq_sendint(&buf, 0, 2);
+			/* Do we have a non-resjunk tlist item? */
+			while (targetlist &&
+				   ((TargetEntry *) lfirst(targetlist))->resdom->resjunk)
+				targetlist = lnext(targetlist);
+			if (targetlist)
+			{
+				Resdom	   *res = ((TargetEntry *) lfirst(targetlist))->resdom;
+
+				pq_sendint(&buf, res->resorigtbl, 4);
+				pq_sendint(&buf, res->resorigcol, 2);
+				targetlist = lnext(targetlist);
+			}
+			else
+			{
+				/* No info available, so send zeroes */
+				pq_sendint(&buf, 0, 4);
+				pq_sendint(&buf, 0, 2);
+			}
 		}
 		pq_sendint(&buf, (int) attrs[i]->atttypid,
 				   sizeof(attrs[i]->atttypid));
@@ -324,7 +345,7 @@ showatts(const char *name, TupleDesc tupleDesc)
  */
 void
 debugSetup(DestReceiver *self, int operation,
-		   const char *portalName, TupleDesc typeinfo)
+		   const char *portalName, TupleDesc typeinfo, List *targetlist)
 {
 	/*
 	 * show the return type of the tuples
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index d117d2e9a23e05f4fb967fb8c37cad3e1db291bd..2504d69fd063d1284848fd7e1b036cb280e43436 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.106 2003/04/24 21:16:42 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.107 2003/05/06 00:20:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -57,8 +57,9 @@ static void show_upper_qual(List *qual, const char *qlabel,
 				const char *outer_name, int outer_varno, Plan *outer_plan,
 				const char *inner_name, int inner_varno, Plan *inner_plan,
 				StringInfo str, int indent, ExplainState *es);
-static void show_sort_keys(List *tlist, int nkeys, const char *qlabel,
-			   StringInfo str, int indent, ExplainState *es);
+static void show_sort_keys(List *tlist, int nkeys, AttrNumber *keycols,
+						   const char *qlabel,
+						   StringInfo str, int indent, ExplainState *es);
 static Node *make_ors_ands_explicit(List *orclauses);
 
 /*
@@ -193,18 +194,10 @@ ExplainOnePlan(QueryDesc *queryDesc, ExplainStmt *stmt,
 	ExplainState *es;
 	StringInfo	str;
 
-	/*
-	 * If we are not going to execute, suppress any SELECT INTO marker.
-	 * Without this, ExecutorStart will create the INTO target table,
-	 * which we don't want.
-	 */
-	if (!stmt->analyze)
-		queryDesc->parsetree->into = NULL;
-
 	gettimeofday(&starttime, NULL);
 
 	/* call ExecutorStart to prepare the plan for execution */
-	ExecutorStart(queryDesc);
+	ExecutorStart(queryDesc, !stmt->analyze);
 
 	/* Execute the plan for statistics if asked for */
 	if (stmt->analyze)
@@ -672,7 +665,9 @@ explain_outNode(StringInfo str,
 							str, indent, es);
 			break;
 		case T_Sort:
-			show_sort_keys(plan->targetlist, ((Sort *) plan)->keycount,
+			show_sort_keys(plan->targetlist,
+						   ((Sort *) plan)->numCols,
+						   ((Sort *) plan)->sortColIdx,
 						   "Sort Key",
 						   str, indent, es);
 			break;
@@ -937,7 +932,8 @@ show_upper_qual(List *qual, const char *qlabel,
  * Show the sort keys for a Sort node.
  */
 static void
-show_sort_keys(List *tlist, int nkeys, const char *qlabel,
+show_sort_keys(List *tlist, int nkeys, AttrNumber *keycols,
+			   const char *qlabel,
 			   StringInfo str, int indent, ExplainState *es)
 {
 	List	   *context;
@@ -985,27 +981,30 @@ show_sort_keys(List *tlist, int nkeys, const char *qlabel,
 	}
 	bms_free(varnos);
 
-	for (keyno = 1; keyno <= nkeys; keyno++)
+	for (keyno = 0; keyno < nkeys; keyno++)
 	{
 		/* find key expression in tlist */
+		AttrNumber	keyresno = keycols[keyno];
+
 		foreach(tl, tlist)
 		{
 			TargetEntry *target = (TargetEntry *) lfirst(tl);
 
-			if (target->resdom->reskey == keyno)
+			if (target->resdom->resno == keyresno)
 			{
 				/* Deparse the expression, showing any top-level cast */
 				exprstr = deparse_expression((Node *) target->expr, context,
 											 useprefix, true);
 				/* And add to str */
-				if (keyno > 1)
+				if (keyno > 0)
 					appendStringInfo(str, ", ");
 				appendStringInfo(str, "%s", exprstr);
 				break;
 			}
 		}
 		if (tl == NIL)
-			elog(ERROR, "show_sort_keys: no tlist entry for key %d", keyno);
+			elog(ERROR, "show_sort_keys: no tlist entry for key %d",
+				 keyresno);
 	}
 
 	appendStringInfo(str, "\n");
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index d70b3379217f29f0dda8e64a9453b4059a5b7f67..719c94565625cd600c3f5c1fcad090811947ad4d 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.206 2003/05/05 17:57:47 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.207 2003/05/06 00:20:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -63,7 +63,7 @@ typedef struct evalPlanQual
 } evalPlanQual;
 
 /* decls for local routines only used within this module */
-static void InitPlan(QueryDesc *queryDesc);
+static void InitPlan(QueryDesc *queryDesc, bool explainOnly);
 static void initResultRelInfo(ResultRelInfo *resultRelInfo,
 				  Index resultRelationIndex,
 				  List *rangeTable,
@@ -104,12 +104,15 @@ static void EvalPlanQualStop(evalPlanQual *epq);
  * field of the QueryDesc is filled in to describe the tuples that will be
  * returned, and the internal fields (estate and planstate) are set up.
  *
+ * If explainOnly is true, we are not actually intending to run the plan,
+ * only to set up for EXPLAIN; so skip unwanted side-effects.
+ *
  * NB: the CurrentMemoryContext when this is called will become the parent
  * of the per-query context used for this Executor invocation.
  * ----------------------------------------------------------------
  */
 void
-ExecutorStart(QueryDesc *queryDesc)
+ExecutorStart(QueryDesc *queryDesc, bool explainOnly)
 {
 	EState	   *estate;
 	MemoryContext oldcontext;
@@ -118,6 +121,13 @@ ExecutorStart(QueryDesc *queryDesc)
 	Assert(queryDesc != NULL);
 	Assert(queryDesc->estate == NULL);
 
+	/*
+	 * If the transaction is read-only, we need to check if any writes
+	 * are planned to non-temporary tables.
+	 */
+	if (!explainOnly)
+		ExecCheckXactReadOnly(queryDesc->parsetree, queryDesc->operation);
+
 	/*
 	 * Build EState, switch into per-query memory context for startup.
 	 */
@@ -149,7 +159,7 @@ ExecutorStart(QueryDesc *queryDesc)
 	/*
 	 * Initialize the plan state tree
 	 */
-	InitPlan(queryDesc);
+	InitPlan(queryDesc, explainOnly);
 
 	MemoryContextSwitchTo(oldcontext);
 }
@@ -202,14 +212,6 @@ ExecutorRun(QueryDesc *queryDesc,
 	operation = queryDesc->operation;
 	dest = queryDesc->dest;
 
-	/*
-	 * If the transaction is read-only, we need to check if any writes
-	 * are planned to non-temporary tables.  This is done here at this
-	 * rather late stage so that we can handle EXPLAIN vs. EXPLAIN
-	 * ANALYZE easily.
-	 */
-	ExecCheckXactReadOnly(queryDesc->parsetree, operation);
-
 	/*
 	 * startup tuple receiver
 	 */
@@ -217,8 +219,10 @@ ExecutorRun(QueryDesc *queryDesc,
 	estate->es_lastoid = InvalidOid;
 
 	destfunc = DestToFunction(dest);
-	(*destfunc->setup) (destfunc, operation, queryDesc->portalName,
-						queryDesc->tupDesc);
+	(*destfunc->setup) (destfunc, operation,
+						queryDesc->portalName,
+						queryDesc->tupDesc,
+						queryDesc->planstate->plan->targetlist);
 
 	/*
 	 * run plan
@@ -468,7 +472,7 @@ fail:
  * ----------------------------------------------------------------
  */
 static void
-InitPlan(QueryDesc *queryDesc)
+InitPlan(QueryDesc *queryDesc, bool explainOnly)
 {
 	CmdType		operation = queryDesc->operation;
 	Query *parseTree = queryDesc->parsetree;
@@ -751,10 +755,12 @@ InitPlan(QueryDesc *queryDesc)
 	 * If doing SELECT INTO, initialize the "into" relation.  We must wait
 	 * till now so we have the "clean" result tuple type to create the
 	 * new table from.
+	 *
+	 * If EXPLAIN, skip creating the "into" relation.
 	 */
 	intoRelationDesc = (Relation) NULL;
 
-	if (do_select_into)
+	if (do_select_into && !explainOnly)
 	{
 		char	   *intoName;
 		Oid			namespaceId;
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 2e7291a006dbb5cace4875a1a7d3c1d2a548cdd8..c81dd33d36a2547dc7f83ee5bca36e435ca47504 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.63 2002/12/13 19:45:52 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.64 2003/05/06 00:20:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -723,7 +723,7 @@ begin_tup_output_tupdesc(CommandDest dest, TupleDesc tupdesc)
 	tstate->destfunc = DestToFunction(dest);
 
 	(*tstate->destfunc->setup) (tstate->destfunc, (int) CMD_SELECT,
-								NULL, tupdesc);
+								NULL, tupdesc, NIL);
 
 	return tstate;
 }
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index d3ccf0dd90576b0c265555843350c6166b03d2f3..ba41828be3493d8ab39b66020891f3156ea20372 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.62 2002/12/15 16:17:46 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.63 2003/05/06 00:20:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -250,7 +250,7 @@ postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
 
 	/* Utility commands don't need Executor. */
 	if (es->qd->operation != CMD_UTILITY)
-		ExecutorStart(es->qd);
+		ExecutorStart(es->qd, false);
 
 	es->status = F_EXEC_RUN;
 }
diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c
index 2be31ce09e809ad9451cfda698a725495cbfcbc8..468b91af9bbc1dbbb19b97beba9281656eae493e 100644
--- a/src/backend/executor/nodeSort.c
+++ b/src/backend/executor/nodeSort.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.43 2003/05/05 17:57:47 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.44 2003/05/06 00:20:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,59 +19,6 @@
 #include "executor/nodeSort.h"
 #include "utils/tuplesort.h"
 
-/* ----------------------------------------------------------------
- *		ExtractSortKeys
- *
- *		Extract the sorting key information from the plan node.
- *
- *		Returns two palloc'd arrays, one of sort operator OIDs and
- *		one of attribute numbers.
- * ----------------------------------------------------------------
- */
-static void
-ExtractSortKeys(Sort *sortnode,
-				Oid **sortOperators,
-				AttrNumber **attNums)
-{
-	List	   *targetList;
-	int			keycount;
-	Oid		   *sortOps;
-	AttrNumber *attNos;
-	List	   *tl;
-
-	/*
-	 * get information from the node
-	 */
-	targetList = sortnode->plan.targetlist;
-	keycount = sortnode->keycount;
-
-	/*
-	 * first allocate space for results
-	 */
-	if (keycount <= 0)
-		elog(ERROR, "ExtractSortKeys: keycount <= 0");
-	sortOps = (Oid *) palloc0(keycount * sizeof(Oid));
-	*sortOperators = sortOps;
-	attNos = (AttrNumber *) palloc0(keycount * sizeof(AttrNumber));
-	*attNums = attNos;
-
-	/*
-	 * extract info from the resdom nodes in the target list
-	 */
-	foreach(tl, targetList)
-	{
-		TargetEntry *target = (TargetEntry *) lfirst(tl);
-		Resdom	   *resdom = target->resdom;
-		Index		reskey = resdom->reskey;
-
-		if (reskey > 0)			/* ignore TLEs that are not sort keys */
-		{
-			Assert(reskey <= keycount);
-			sortOps[reskey - 1] = resdom->reskeyop;
-			attNos[reskey - 1] = resdom->resno;
-		}
-	}
-}
 
 /* ----------------------------------------------------------------
  *		ExecSort
@@ -118,8 +65,6 @@ ExecSort(SortState *node)
 		Sort	   *plannode = (Sort *) node->ss.ps.plan;
 		PlanState  *outerNode;
 		TupleDesc	tupDesc;
-		Oid		   *sortOperators;
-		AttrNumber *attNums;
 
 		SO1_printf("ExecSort: %s\n",
 				   "sorting subplan");
@@ -139,16 +84,13 @@ ExecSort(SortState *node)
 		outerNode = outerPlanState(node);
 		tupDesc = ExecGetResultType(outerNode);
 
-		ExtractSortKeys(plannode, &sortOperators, &attNums);
-
-		tuplesortstate = tuplesort_begin_heap(tupDesc, plannode->keycount,
-											  sortOperators, attNums,
+		tuplesortstate = tuplesort_begin_heap(tupDesc,
+											  plannode->numCols,
+											  plannode->sortOperators,
+											  plannode->sortColIdx,
 											  true /* randomAccess */ );
 		node->tuplesortstate = (void *) tuplesortstate;
 
-		pfree(sortOperators);
-		pfree(attNums);
-
 		/*
 		 * Scan the subplan and feed all the tuples to tuplesort.
 		 */
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 3b1e6c4bb3f99472ebd7e7b624e910c41891c6c1..74299ddf3ea9fe24928cdc2c743f775500211706 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.94 2003/05/02 20:54:34 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.95 2003/05/06 00:20:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -880,7 +880,7 @@ SPI_cursor_close(Portal portal)
  */
 void
 spi_dest_setup(DestReceiver *self, int operation,
-			   const char *portalName, TupleDesc typeinfo)
+			   const char *portalName, TupleDesc typeinfo, List *targetlist)
 {
 	SPITupleTable *tuptable;
 	MemoryContext oldcxt;
@@ -1209,7 +1209,7 @@ _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount)
 		ResetUsage();
 #endif
 
-	ExecutorStart(queryDesc);
+	ExecutorStart(queryDesc, false);
 
 	ExecutorRun(queryDesc, ForwardScanDirection, (long) tcount);
 
diff --git a/src/backend/executor/tstoreReceiver.c b/src/backend/executor/tstoreReceiver.c
index c4d16ef5e9846081e799b42f1bef7313501fb6ca..05b0c1f2397660282299ef6eb2f24528e869fe08 100644
--- a/src/backend/executor/tstoreReceiver.c
+++ b/src/backend/executor/tstoreReceiver.c
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/tstoreReceiver.c,v 1.3 2003/05/02 20:54:34 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/tstoreReceiver.c,v 1.4 2003/05/06 00:20:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,7 +40,8 @@ typedef struct
  */
 static void
 tstoreSetupReceiver(DestReceiver *self, int operation,
-					const char *portalname, TupleDesc typeinfo)
+					const char *portalname,
+					TupleDesc typeinfo, List *targetlist)
 {
 	TStoreState *myState = (TStoreState *) self;
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 4c773657fe49bfed6e3b159bcfd147255df465e9..1dc79a4dc40834121169a4231b34f788097d1287 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.249 2003/05/02 20:54:34 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.250 2003/05/06 00:20:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -448,7 +448,9 @@ _copySort(Sort *from)
 	 */
 	CopyPlanFields((Plan *) from, (Plan *) newnode);
 
-	COPY_SCALAR_FIELD(keycount);
+	COPY_SCALAR_FIELD(numCols);
+	COPY_POINTER_FIELD(sortColIdx, from->numCols * sizeof(AttrNumber));
+	COPY_POINTER_FIELD(sortOperators, from->numCols * sizeof(Oid));
 
 	return newnode;
 }
@@ -596,8 +598,8 @@ _copyResdom(Resdom *from)
 	COPY_SCALAR_FIELD(restypmod);
 	COPY_STRING_FIELD(resname);
 	COPY_SCALAR_FIELD(ressortgroupref);
-	COPY_SCALAR_FIELD(reskey);
-	COPY_SCALAR_FIELD(reskeyop);
+	COPY_SCALAR_FIELD(resorigtbl);
+	COPY_SCALAR_FIELD(resorigcol);
 	COPY_SCALAR_FIELD(resjunk);
 
 	return newnode;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index e4c10ed968665f4759c3e8eaf5ed413bddf3eff2..6b9cac028b990d87b128231ca10adf7f579c7188 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.192 2003/05/02 20:54:34 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.193 2003/05/06 00:20:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -104,8 +104,8 @@ _equalResdom(Resdom *a, Resdom *b)
 	COMPARE_SCALAR_FIELD(restypmod);
 	COMPARE_STRING_FIELD(resname);
 	COMPARE_SCALAR_FIELD(ressortgroupref);
-	COMPARE_SCALAR_FIELD(reskey);
-	COMPARE_SCALAR_FIELD(reskeyop);
+	COMPARE_SCALAR_FIELD(resorigtbl);
+	COMPARE_SCALAR_FIELD(resorigcol);
 	COMPARE_SCALAR_FIELD(resjunk);
 
 	return true;
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index e9f10720cbb2b0bb29bc07ac8e96b7a337751e39..4683d36bfbcdf4a16b078c01601dd0f5aa59ea10 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.38 2003/02/10 04:44:45 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.39 2003/05/06 00:20:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -117,16 +117,16 @@ makeResdom(AttrNumber resno,
 	resdom->resname = resname;
 
 	/*
-	 * We always set the sorting/grouping fields to 0.	If the caller
-	 * wants to change them he must do so explicitly.  Few if any callers
-	 * should be doing that, so omitting these arguments reduces the
-	 * chance of error.
+	 * We always set these fields to 0. If the caller wants to change them
+	 * he must do so explicitly.  Few callers do that, so omitting these
+	 * arguments reduces the chance of error.
 	 */
 	resdom->ressortgroupref = 0;
-	resdom->reskey = 0;
-	resdom->reskeyop = InvalidOid;
+	resdom->resorigtbl = InvalidOid;
+	resdom->resorigcol = 0;
 
 	resdom->resjunk = resjunk;
+
 	return resdom;
 }
 
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 654905b09628138f92dc6dc7ae8578b51e8bc1bb..377c6bd2297b54fbb4490ed8f9af6d35e4999ace 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.204 2003/05/02 20:54:34 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.205 2003/05/06 00:20:32 tgl Exp $
  *
  * NOTES
  *	  Every node type that can appear in stored rules' parsetrees *must*
@@ -420,11 +420,17 @@ _outAgg(StringInfo str, Agg *node)
 static void
 _outGroup(StringInfo str, Group *node)
 {
-	WRITE_NODE_TYPE("GRP");
+	int			i;
+
+	WRITE_NODE_TYPE("GROUP");
 
 	_outPlanInfo(str, (Plan *) node);
 
 	WRITE_INT_FIELD(numCols);
+
+	appendStringInfo(str, " :grpColIdx");
+	for (i = 0; i < node->numCols; i++)
+		appendStringInfo(str, " %d", node->grpColIdx[i]);
 }
 
 static void
@@ -438,11 +444,21 @@ _outMaterial(StringInfo str, Material *node)
 static void
 _outSort(StringInfo str, Sort *node)
 {
+	int			i;
+
 	WRITE_NODE_TYPE("SORT");
 
 	_outPlanInfo(str, (Plan *) node);
 
-	WRITE_INT_FIELD(keycount);
+	WRITE_INT_FIELD(numCols);
+
+	appendStringInfo(str, " :sortColIdx");
+	for (i = 0; i < node->numCols; i++)
+		appendStringInfo(str, " %d", node->sortColIdx[i]);
+
+	appendStringInfo(str, " :sortOperators");
+	for (i = 0; i < node->numCols; i++)
+		appendStringInfo(str, " %u", node->sortOperators[i]);
 }
 
 static void
@@ -517,8 +533,8 @@ _outResdom(StringInfo str, Resdom *node)
 	WRITE_INT_FIELD(restypmod);
 	WRITE_STRING_FIELD(resname);
 	WRITE_UINT_FIELD(ressortgroupref);
-	WRITE_UINT_FIELD(reskey);
-	WRITE_OID_FIELD(reskeyop);
+	WRITE_OID_FIELD(resorigtbl);
+	WRITE_INT_FIELD(resorigcol);
 	WRITE_BOOL_FIELD(resjunk);
 }
 
diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c
index ccfa923c726612c155aef64712597cb86faf9e37..6e8c0d7bf079a89d9264be86f10a13d31b9da3ae 100644
--- a/src/backend/nodes/print.c
+++ b/src/backend/nodes/print.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.60 2003/01/22 19:26:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.61 2003/05/06 00:20:32 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -447,8 +447,8 @@ print_tl(List *tlist, List *rtable)
 		TargetEntry *tle = lfirst(tl);
 
 		printf("\t%d %s\t", tle->resdom->resno, tle->resdom->resname);
-		if (tle->resdom->reskey != 0)
-			printf("(%d):\t", tle->resdom->reskey);
+		if (tle->resdom->ressortgroupref != 0)
+			printf("(%u):\t", tle->resdom->ressortgroupref);
 		else
 			printf("    :\t");
 		print_expr((Node *) tle->expr, rtable);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 3c8e7501f434c6626e8b369a9918f44c33f31118..68daca4b5551557b25b113539b2d56543bbe0482 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.152 2003/05/02 20:54:34 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.153 2003/05/06 00:20:32 tgl Exp $
  *
  * NOTES
  *	  Path and Plan nodes do not have any readfuncs support, because we
@@ -310,8 +310,8 @@ _readResdom(void)
 	READ_INT_FIELD(restypmod);
 	READ_STRING_FIELD(resname);
 	READ_UINT_FIELD(ressortgroupref);
-	READ_UINT_FIELD(reskey);
-	READ_OID_FIELD(reskeyop);
+	READ_OID_FIELD(resorigtbl);
+	READ_INT_FIELD(resorigcol);
 	READ_BOOL_FIELD(resjunk);
 
 	READ_DONE();
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index d01acdc6182afb172913718bc904bcc2cea1378c..b491065f03df42f93e9a5b97a16edd33e0b932ba 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.138 2003/03/10 03:53:50 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.139 2003/05/06 00:20:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -101,6 +101,8 @@ static MergeJoin *make_mergejoin(List *tlist,
 			   List *mergeclauses,
 			   Plan *lefttree, Plan *righttree,
 			   JoinType jointype);
+static Sort *make_sort(Query *root, List *tlist, Plan *lefttree, int numCols,
+					   AttrNumber *sortColIdx, Oid *sortOperators);
 static Sort *make_sort_from_pathkeys(Query *root, Plan *lefttree,
 									 Relids relids, List *pathkeys);
 
@@ -576,7 +578,7 @@ create_unique_plan(Query *root, UniquePath *best_path)
 			subplan->targetlist = newtlist;
 	}
 
-	my_tlist = new_unsorted_tlist(subplan->targetlist);
+	my_tlist = copyObject(subplan->targetlist);
 
 	if (best_path->use_hash)
 	{
@@ -1614,13 +1616,13 @@ make_mergejoin(List *tlist,
 }
 
 /*
- * To use make_sort directly, you must already have marked the tlist
- * with reskey and reskeyop information.  The keys had better be
- * non-redundant, too (ie, there had better be tlist items marked with
- * each key number from 1 to keycount), or the executor will get confused!
+ * make_sort --- basic routine to build a Sort plan node
+ *
+ * Caller must have built the sortColIdx and sortOperators arrays already.
  */
-Sort *
-make_sort(Query *root, List *tlist, Plan *lefttree, int keycount)
+static Sort *
+make_sort(Query *root, List *tlist, Plan *lefttree, int numCols,
+		  AttrNumber *sortColIdx, Oid *sortOperators)
 {
 	Sort	   *node = makeNode(Sort);
 	Plan	   *plan = &node->plan;
@@ -1637,11 +1639,43 @@ make_sort(Query *root, List *tlist, Plan *lefttree, int keycount)
 	plan->qual = NIL;
 	plan->lefttree = lefttree;
 	plan->righttree = NULL;
-	node->keycount = keycount;
+	node->numCols = numCols;
+	node->sortColIdx = sortColIdx;
+	node->sortOperators = sortOperators;
 
 	return node;
 }
 
+/*
+ * add_sort_column --- utility subroutine for building sort info arrays
+ *
+ * We need this routine because the same column might be selected more than
+ * once as a sort key column; if so, the extra mentions are redundant.
+ *
+ * Caller is assumed to have allocated the arrays large enough for the
+ * max possible number of columns.  Return value is the new column count.
+ */
+static int
+add_sort_column(AttrNumber colIdx, Oid sortOp,
+				int numCols, AttrNumber *sortColIdx, Oid *sortOperators)
+{
+	int			i;
+
+	for (i = 0; i < numCols; i++)
+	{
+		if (sortColIdx[i] == colIdx)
+		{
+			/* Already sorting by this col, so extra sort key is useless */
+			return numCols;
+		}
+	}
+
+	/* Add the column */
+	sortColIdx[numCols] = colIdx;
+	sortOperators[numCols] = sortOp;
+	return numCols + 1;
+}
+
 /*
  * make_sort_from_pathkeys
  *	  Create sort plan to sort according to given pathkeys
@@ -1650,8 +1684,8 @@ make_sort(Query *root, List *tlist, Plan *lefttree, int keycount)
  *	  'relids' is the set of relids represented by the input node
  *	  'pathkeys' is the list of pathkeys by which the result is to be sorted
  *
- * We must convert the pathkey information into reskey and reskeyop fields
- * of resdom nodes in the sort plan's target list.
+ * We must convert the pathkey information into arrays of sort key column
+ * numbers and sort operator OIDs.
  *
  * If the pathkeys include expressions that aren't simple Vars, we will
  * usually need to add resjunk items to the input plan's targetlist to
@@ -1666,10 +1700,16 @@ make_sort_from_pathkeys(Query *root, Plan *lefttree,
 	List	   *tlist = lefttree->targetlist;
 	List	   *sort_tlist;
 	List	   *i;
-	int			numsortkeys = 0;
+	int			numsortkeys;
+	AttrNumber *sortColIdx;
+	Oid		   *sortOperators;
 
-	/* Create a new target list for the sort, with sort keys set. */
-	sort_tlist = new_unsorted_tlist(tlist);
+	/* We will need at most length(pathkeys) sort columns; possibly less */
+	numsortkeys = length(pathkeys);
+	sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber));
+	sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid));
+
+	numsortkeys = 0;
 
 	foreach(i, pathkeys)
 	{
@@ -1681,7 +1721,7 @@ make_sort_from_pathkeys(Query *root, Plan *lefttree,
 		/*
 		 * We can sort by any one of the sort key items listed in this
 		 * sublist.  For now, we take the first one that corresponds to an
-		 * available Var in the sort_tlist.  If there isn't any, use the
+		 * available Var in the tlist.  If there isn't any, use the
 		 * first one that is an expression in the input's vars.
 		 *
 		 * XXX if we have a choice, is there any way of figuring out which
@@ -1694,7 +1734,7 @@ make_sort_from_pathkeys(Query *root, Plan *lefttree,
 		{
 			pathkey = lfirst(j);
 			Assert(IsA(pathkey, PathKeyItem));
-			resdom = tlist_member(pathkey->key, sort_tlist);
+			resdom = tlist_member(pathkey->key, tlist);
 			if (resdom)
 				break;
 		}
@@ -1717,7 +1757,7 @@ make_sort_from_pathkeys(Query *root, Plan *lefttree,
 			 */
 			if (IsA(lefttree, Append))
 			{
-				tlist = new_unsorted_tlist(tlist);
+				tlist = copyObject(tlist);
 				lefttree = (Plan *) make_result(tlist, NULL, lefttree);
 			}
 			/*
@@ -1732,38 +1772,24 @@ make_sort_from_pathkeys(Query *root, Plan *lefttree,
 							makeTargetEntry(resdom,
 											(Expr *) pathkey->key));
 			lefttree->targetlist = tlist; /* just in case NIL before */
-			/*
-			 * Add one to sort node's tlist too.  This will be identical
-			 * except we are going to set the sort key info in it.
-			 */
-			resdom = makeResdom(length(sort_tlist) + 1,
-								exprType(pathkey->key),
-								exprTypmod(pathkey->key),
-								NULL,
-								true);
-			sort_tlist = lappend(sort_tlist,
-								 makeTargetEntry(resdom,
-												 (Expr *) pathkey->key));
 		}
 		/*
-		 * The resdom might be already marked as a sort key, if the
+		 * The column might already be selected as a sort key, if the
 		 * pathkeys contain duplicate entries.	(This can happen in
 		 * scenarios where multiple mergejoinable clauses mention the same
-		 * var, for example.) In that case the current pathkey is
-		 * essentially a no-op, because only one value can be seen within
-		 * any subgroup where it would be consulted.  We can ignore it.
+		 * var, for example.)  So enter it only once in the sort arrays.
 		 */
-		if (resdom->reskey == 0)
-		{
-			/* OK, mark it as a sort key and set the sort operator */
-			resdom->reskey = ++numsortkeys;
-			resdom->reskeyop = pathkey->sortop;
-		}
+		numsortkeys = add_sort_column(resdom->resno, pathkey->sortop,
+									  numsortkeys, sortColIdx, sortOperators);
 	}
 
 	Assert(numsortkeys > 0);
 
-	return make_sort(root, sort_tlist, lefttree, numsortkeys);
+	/* Give Sort node its own copy of the tlist (still necessary?) */
+	sort_tlist = copyObject(tlist);
+
+	return make_sort(root, sort_tlist, lefttree, numsortkeys,
+					 sortColIdx, sortOperators);
 }
 
 /*
@@ -1780,36 +1806,96 @@ make_sort_from_sortclauses(Query *root, List *tlist,
 {
 	List	   *sort_tlist;
 	List	   *i;
-	int			keyno = 0;
+	int			numsortkeys;
+	AttrNumber *sortColIdx;
+	Oid		   *sortOperators;
 
-	/*
-	 * First make a copy of the tlist so that we don't corrupt the
-	 * original.
-	 */
-	sort_tlist = new_unsorted_tlist(tlist);
+	/* We will need at most length(sortcls) sort columns; possibly less */
+	numsortkeys = length(sortcls);
+	sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber));
+	sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid));
+
+	numsortkeys = 0;
 
 	foreach(i, sortcls)
 	{
 		SortClause *sortcl = (SortClause *) lfirst(i);
-		TargetEntry *tle = get_sortgroupclause_tle(sortcl, sort_tlist);
+		TargetEntry *tle = get_sortgroupclause_tle(sortcl, tlist);
 		Resdom	   *resdom = tle->resdom;
 
 		/*
 		 * Check for the possibility of duplicate order-by clauses --- the
-		 * parser should have removed 'em, but the executor will get
-		 * terribly confused if any get through!
+		 * parser should have removed 'em, but no point in sorting redundantly.
 		 */
-		if (resdom->reskey == 0)
-		{
-			/* OK, insert the ordering info needed by the executor. */
-			resdom->reskey = ++keyno;
-			resdom->reskeyop = sortcl->sortop;
-		}
+		numsortkeys = add_sort_column(resdom->resno, sortcl->sortop,
+									  numsortkeys, sortColIdx, sortOperators);
 	}
 
-	Assert(keyno > 0);
+	Assert(numsortkeys > 0);
+
+	/* Give Sort node its own copy of the tlist (still necessary?) */
+	sort_tlist = copyObject(tlist);
+
+	return make_sort(root, sort_tlist, lefttree, numsortkeys,
+					 sortColIdx, sortOperators);
+}
+
+/*
+ * make_sort_from_groupcols
+ *	  Create sort plan to sort based on grouping columns
+ *
+ * 'groupcls' is the list of GroupClauses
+ * 'grpColIdx' gives the column numbers to use
+ *
+ * This might look like it could be merged with make_sort_from_sortclauses,
+ * but presently we *must* use the grpColIdx[] array to locate sort columns,
+ * because the child plan's tlist is not marked with ressortgroupref info
+ * appropriate to the grouping node.  So, only the sortop is used from the
+ * GroupClause entries.
+ */
+Sort *
+make_sort_from_groupcols(Query *root,
+						 List *groupcls,
+						 AttrNumber *grpColIdx,
+						 Plan *lefttree)
+{
+	List	   *sub_tlist = lefttree->targetlist;
+	List	   *sort_tlist;
+	int			grpno = 0;
+	List	   *i;
+	int			numsortkeys;
+	AttrNumber *sortColIdx;
+	Oid		   *sortOperators;
+
+	/* We will need at most length(groupcls) sort columns; possibly less */
+	numsortkeys = length(groupcls);
+	sortColIdx = (AttrNumber *) palloc(numsortkeys * sizeof(AttrNumber));
+	sortOperators = (Oid *) palloc(numsortkeys * sizeof(Oid));
+
+	numsortkeys = 0;
+
+	foreach(i, groupcls)
+	{
+		GroupClause *grpcl = (GroupClause *) lfirst(i);
+		TargetEntry *tle = nth(grpColIdx[grpno] - 1, sub_tlist);
+		Resdom	   *resdom = tle->resdom;
+
+		/*
+		 * Check for the possibility of duplicate group-by clauses --- the
+		 * parser should have removed 'em, but no point in sorting redundantly.
+		 */
+		numsortkeys = add_sort_column(resdom->resno, grpcl->sortop,
+									  numsortkeys, sortColIdx, sortOperators);
+		grpno++;
+	}
+
+	Assert(numsortkeys > 0);
+
+	/* Give Sort node its own copy of the tlist (still necessary?) */
+	sort_tlist = copyObject(sub_tlist);
 
-	return make_sort(root, sort_tlist, lefttree, keyno);
+	return make_sort(root, sort_tlist, lefttree, numsortkeys,
+					 sortColIdx, sortOperators);
 }
 
 Material *
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index e53a18ac296e07ff7e763c81d837fe58d8f44aab..eca7a908f7a5ab57cafa58343092e7069cf1c260 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.152 2003/03/13 16:58:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.153 2003/05/06 00:20:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -61,10 +61,6 @@ static void locate_grouping_columns(Query *parse,
 									List *tlist,
 									List *sub_tlist,
 									AttrNumber *groupColIdx);
-static Plan *make_groupsortplan(Query *parse,
-								List *groupClause,
-								AttrNumber *grpColIdx,
-								Plan *subplan);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
 
 
@@ -1145,10 +1141,11 @@ grouping_planner(Query *parse, double tuple_fraction)
 			{
 				if (!pathkeys_contained_in(group_pathkeys, current_pathkeys))
 				{
-					result_plan = make_groupsortplan(parse,
-													 parse->groupClause,
-													 groupColIdx,
-													 result_plan);
+					result_plan = (Plan *)
+						make_sort_from_groupcols(parse,
+												 parse->groupClause,
+												 groupColIdx,
+												 result_plan);
 					current_pathkeys = group_pathkeys;
 				}
 				aggstrategy = AGG_SORTED;
@@ -1193,10 +1190,11 @@ grouping_planner(Query *parse, double tuple_fraction)
 				 */
 				if (!pathkeys_contained_in(group_pathkeys, current_pathkeys))
 				{
-					result_plan = make_groupsortplan(parse,
-													 parse->groupClause,
-													 groupColIdx,
-													 result_plan);
+					result_plan = (Plan *)
+						make_sort_from_groupcols(parse,
+												 parse->groupClause,
+												 groupColIdx,
+												 result_plan);
 					current_pathkeys = group_pathkeys;
 				}
 
@@ -1219,10 +1217,11 @@ grouping_planner(Query *parse, double tuple_fraction)
 	{
 		if (!pathkeys_contained_in(sort_pathkeys, current_pathkeys))
 		{
-			result_plan = (Plan *) make_sort_from_sortclauses(parse,
-															  tlist,
-															  result_plan,
-															  parse->sortClause);
+			result_plan = (Plan *)
+				make_sort_from_sortclauses(parse,
+										   tlist,
+										   result_plan,
+										   parse->sortClause);
 			current_pathkeys = sort_pathkeys;
 		}
 	}
@@ -1471,53 +1470,6 @@ locate_grouping_columns(Query *parse,
 	}
 }
 
-/*
- * make_groupsortplan
- *		Add a Sort node to explicitly sort according to the GROUP BY clause.
- *
- * Note: the Sort node always just takes a copy of the subplan's tlist
- * plus ordering information.  (This might seem inefficient if the
- * subplan contains complex GROUP BY expressions, but in fact Sort
- * does not evaluate its targetlist --- it only outputs the same
- * tuples in a new order.  So the expressions we might be copying
- * are just dummies with no extra execution cost.)
- */
-static Plan *
-make_groupsortplan(Query *parse,
-				   List *groupClause,
-				   AttrNumber *grpColIdx,
-				   Plan *subplan)
-{
-	List	   *sort_tlist = new_unsorted_tlist(subplan->targetlist);
-	int			grpno = 0;
-	int			keyno = 0;
-	List	   *gl;
-
-	foreach(gl, groupClause)
-	{
-		GroupClause *grpcl = (GroupClause *) lfirst(gl);
-		TargetEntry *te = nth(grpColIdx[grpno] - 1, sort_tlist);
-		Resdom	   *resdom = te->resdom;
-
-		/*
-		 * Check for the possibility of duplicate group-by clauses ---
-		 * the parser should have removed 'em, but the Sort executor
-		 * will get terribly confused if any get through!
-		 */
-		if (resdom->reskey == 0)
-		{
-			/* OK, insert the ordering info needed by the executor. */
-			resdom->reskey = ++keyno;
-			resdom->reskeyop = grpcl->sortop;
-		}
-		grpno++;
-	}
-
-	Assert(keyno > 0);
-
-	return (Plan *) make_sort(parse, sort_tlist, subplan, keyno);
-}
-
 /*
  * postprocess_setop_tlist
  *	  Fix up targetlist returned by plan_set_operations().
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index d2b91c2ec6d23510c92c9e6c6fca594ad830e57b..86a52645fe674a0571c2a8c9c0d122c247e609d1 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -14,7 +14,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.94 2003/04/29 22:13:09 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.95 2003/05/06 00:20:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -238,7 +238,7 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
 	{
 		List	   *sortList;
 
-		tlist = new_unsorted_tlist(tlist);
+		tlist = copyObject(tlist);
 		sortList = addAllTargetsToSortList(NIL, tlist);
 		plan = (Plan *) make_sort_from_sortclauses(parse, tlist,
 												   plan, sortList);
@@ -292,7 +292,7 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
 	 * Sort the child results, then add a SetOp plan node to generate the
 	 * correct output.
 	 */
-	tlist = new_unsorted_tlist(tlist);
+	tlist = copyObject(tlist);
 	sortList = addAllTargetsToSortList(NIL, tlist);
 	plan = (Plan *) make_sort_from_sortclauses(parse, tlist, plan, sortList);
 	switch (op->op)
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index 53d5615cb0b8879f343d4c88c4a7063a73c227a2..9b10e8e97be34fc03b09c6460a458570e25472b9 100644
--- a/src/backend/optimizer/util/tlist.c
+++ b/src/backend/optimizer/util/tlist.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.55 2003/02/15 20:12:40 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.56 2003/05/06 00:20:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -117,32 +117,6 @@ create_tl_element(Var *var, int resdomno)
  *		---------- GENERAL target list routines ----------
  *****************************************************************************/
 
-/*
- * new_unsorted_tlist
- *	  Creates a copy of a target list by creating new resdom nodes
- *	  without sort information.
- *
- * 'targetlist' is the target list to be copied.
- *
- * Returns the resulting target list.
- *
- */
-List *
-new_unsorted_tlist(List *targetlist)
-{
-	List	   *new_targetlist = (List *) copyObject((Node *) targetlist);
-	List	   *x;
-
-	foreach(x, new_targetlist)
-	{
-		TargetEntry *tle = (TargetEntry *) lfirst(x);
-
-		tle->resdom->reskey = 0;
-		tle->resdom->reskeyop = (Oid) 0;
-	}
-	return new_targetlist;
-}
-
 /*
  * flatten_tlist
  *	  Create a target list that only contains unique variables.
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index ad2d5ab5681b9be914f340fd2d4a8a70fc65ee61..b7fc22c46bea5d45c9e5f182fd116e70a249ac92 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.270 2003/05/05 00:44:55 tgl Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.271 2003/05/06 00:20:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1747,6 +1747,9 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 	if (stmt->intoColNames)
 		applyColumnNames(qry->targetList, stmt->intoColNames);
 
+	/* mark column origins */
+	markTargetListOrigins(pstate, qry->targetList);
+
 	/* transform WHERE */
 	qual = transformWhereClause(pstate, stmt->whereClause);
 
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index dc8f241d45ea6e03450f4ace313a3c997a8b11ca..10892bc292d73014c9645a36e2b2b25ad30150d9 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.100 2003/04/29 22:13:10 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.101 2003/05/06 00:20:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,6 +26,7 @@
 #include "utils/builtins.h"
 
 
+static void markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var);
 static List *ExpandAllTables(ParseState *pstate);
 static char *FigureColname(Node *node);
 static int	FigureColnameInternal(Node *node, char **name);
@@ -204,6 +205,94 @@ transformTargetList(ParseState *pstate, List *targetlist)
 }
 
 
+/*
+ * markTargetListOrigins()
+ *		Mark targetlist columns that are simple Vars with the source
+ *		table's OID and column number.
+ *
+ * Currently, this is done only for SELECT targetlists, since we only
+ * need the info if we are going to send it to the frontend.
+ */
+void
+markTargetListOrigins(ParseState *pstate, List *targetlist)
+{
+	List	   *l;
+
+	foreach(l, targetlist)
+	{
+		TargetEntry *tle = (TargetEntry *) lfirst(l);
+
+		markTargetListOrigin(pstate, tle->resdom, (Var *) tle->expr);
+	}
+}
+
+/*
+ * markTargetListOrigin()
+ *		If 'var' is a Var of a plain relation, mark 'res' with its origin
+ *
+ * This is split out so it can recurse for join references.  Note that we
+ * do not drill down into views, but report the view as the column owner.
+ */
+static void
+markTargetListOrigin(ParseState *pstate, Resdom *res, Var *var)
+{
+	RangeTblEntry *rte;
+	AttrNumber	attnum;
+
+	if (var == NULL || !IsA(var, Var))
+		return;
+	Assert(var->varno > 0 &&
+		   (int) var->varno <= length(pstate->p_rtable));
+	rte = rt_fetch(var->varno, pstate->p_rtable);
+	attnum = var->varattno;
+
+	switch (rte->rtekind)
+	{
+		case RTE_RELATION:
+			/* It's a table or view, report it */
+			res->resorigtbl = rte->relid;
+			res->resorigcol = attnum;
+			break;
+		case RTE_SUBQUERY:
+			{
+				/* Subselect-in-FROM: copy up from the subselect */
+				List	   *subtl;
+
+				foreach(subtl, rte->subquery->targetList)
+				{
+					TargetEntry *subte = (TargetEntry *) lfirst(subtl);
+
+					if (subte->resdom->resjunk ||
+						subte->resdom->resno != attnum)
+						continue;
+					res->resorigtbl = subte->resdom->resorigtbl;
+					res->resorigcol = subte->resdom->resorigcol;
+					break;
+				}
+				/* falling off end of list shouldn't happen... */
+				if (subtl == NIL)
+					elog(ERROR, "Subquery %s does not have attribute %d",
+						 rte->eref->aliasname, attnum);
+			}
+			break;
+		case RTE_JOIN:
+			{
+				/* Join RTE --- recursively inspect the alias variable */
+				Var	   *aliasvar;
+
+				Assert(attnum > 0 && attnum <= length(rte->joinaliasvars));
+				aliasvar = (Var *) nth(attnum - 1, rte->joinaliasvars);
+				markTargetListOrigin(pstate, res, aliasvar);
+			}
+			break;
+		case RTE_SPECIAL:
+		case RTE_FUNCTION:
+			/* not a simple relation, leave it unmarked */
+			break;
+	}
+}
+
+
 /*
  * updateTargetListEntry()
  *	This is used in INSERT and UPDATE statements only.	It prepares a
diff --git a/src/backend/tcop/dest.c b/src/backend/tcop/dest.c
index a5905dedc7fd93ebfb6bf252057e36d6cc6f02a1..54b5ef75c1a530fca7153b8ec10ae7a95c4ced8d 100644
--- a/src/backend/tcop/dest.c
+++ b/src/backend/tcop/dest.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.55 2003/05/05 00:44:56 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/dest.c,v 1.56 2003/05/06 00:20:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -46,7 +46,7 @@ donothingReceive(HeapTuple tuple, TupleDesc typeinfo, DestReceiver *self)
 
 static void
 donothingSetup(DestReceiver *self, int operation,
-			   const char *portalName, TupleDesc typeinfo)
+			   const char *portalName, TupleDesc typeinfo, List *targetlist)
 {
 }
 
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index d57ccd973b2665d3ef199119ce1cea3baad9c51e..d9172655e2a22c5d798e70292f709887c18452f2 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.332 2003/05/05 00:44:56 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.333 2003/05/06 00:20:33 tgl Exp $
  *
  * NOTES
  *	  this is the "main" module of the postgres backend and
@@ -1460,7 +1460,15 @@ exec_describe_portal_message(const char *portal_name)
 		return;					/* can't actually do anything... */
 
 	if (portal->tupDesc)
-		SendRowDescriptionMessage(portal->tupDesc);
+	{
+		List   *targetlist;
+
+		if (portal->strategy == PORTAL_ONE_SELECT)
+			targetlist = ((Plan *) lfirst(portal->planTrees))->targetlist;
+		else
+			targetlist = NIL;
+		SendRowDescriptionMessage(portal->tupDesc, targetlist);
+	}
 	else
 		pq_putemptymessage('n');	/* NoData */
 }
@@ -2335,7 +2343,7 @@ PostgresMain(int argc, char *argv[], const char *username)
 	if (!IsUnderPostmaster)
 	{
 		puts("\nPOSTGRES backend interactive interface ");
-		puts("$Revision: 1.332 $ $Date: 2003/05/05 00:44:56 $\n");
+		puts("$Revision: 1.333 $ $Date: 2003/05/06 00:20:33 $\n");
 	}
 
 	/*
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index f70b91322445713f14921f0930037dd5b2b11acf..280f269c8f8fadf6feddd12f0ae4e052eebcb878 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.60 2003/05/02 20:54:35 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.61 2003/05/06 00:20:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -136,7 +136,7 @@ ProcessQuery(Query *parsetree,
 	/*
 	 * Call ExecStart to prepare the plan for execution
 	 */
-	ExecutorStart(queryDesc);
+	ExecutorStart(queryDesc, false);
 
 	/*
 	 * Run the plan to completion.
@@ -256,7 +256,7 @@ PortalStart(Portal portal, ParamListInfo params)
 			/*
 			 * Call ExecStart to prepare the plan for execution
 			 */
-			ExecutorStart(queryDesc);
+			ExecutorStart(queryDesc, false);
 			/*
 			 * This tells PortalCleanup to shut down the executor
 			 */
@@ -571,10 +571,18 @@ RunFromStore(Portal portal, ScanDirection direction, long count,
 			 CommandDest dest)
 {
 	DestReceiver *destfunc;
+	List	   *targetlist;
 	long		current_tuple_count = 0;
 
 	destfunc = DestToFunction(dest);
-	(*destfunc->setup) (destfunc, CMD_SELECT, portal->name, portal->tupDesc);
+
+	if (portal->strategy == PORTAL_ONE_SELECT)
+		targetlist = ((Plan *) lfirst(portal->planTrees))->targetlist;
+	else
+		targetlist = NIL;
+
+	(*destfunc->setup) (destfunc, CMD_SELECT, portal->name, portal->tupDesc,
+						targetlist);
 
 	if (direction == NoMovementScanDirection)
 	{
diff --git a/src/include/access/printtup.h b/src/include/access/printtup.h
index 688a75cd2db48b340c050472309e3d3660428aac..c4c92fb1bb677022694ac519b42e038d8baf6f99 100644
--- a/src/include/access/printtup.h
+++ b/src/include/access/printtup.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: printtup.h,v 1.24 2003/05/05 00:44:56 tgl Exp $
+ * $Id: printtup.h,v 1.25 2003/05/06 00:20:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,16 +18,16 @@
 
 extern DestReceiver *printtup_create_DR(bool isBinary, bool sendDescrip);
 
-extern void SendRowDescriptionMessage(TupleDesc typeinfo);
+extern void SendRowDescriptionMessage(TupleDesc typeinfo, List *targetlist);
 
 extern void debugSetup(DestReceiver *self, int operation,
-		   const char *portalName, TupleDesc typeinfo);
+		   const char *portalName, TupleDesc typeinfo, List *targetlist);
 extern void debugtup(HeapTuple tuple, TupleDesc typeinfo,
 		 DestReceiver *self);
 
 /* XXX these are really in executor/spi.c */
 extern void spi_dest_setup(DestReceiver *self, int operation,
-		   const char *portalName, TupleDesc typeinfo);
+		   const char *portalName, TupleDesc typeinfo, List *targetlist);
 extern void spi_printtup(HeapTuple tuple, TupleDesc tupdesc,
 			 DestReceiver *self);
 
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 0c14811682d4e053e8b5044031d168452043eb62..74e36730c5432a73fe110afaeec20041b710512c 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: catversion.h,v 1.185 2003/05/02 20:54:35 tgl Exp $
+ * $Id: catversion.h,v 1.186 2003/05/06 00:20:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200305011
+#define CATALOG_VERSION_NO	200305051
 
 #endif
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 9693435977d15fef28c244d36531c32b9ed93613..302bc2681ceaa0bf4a8b2a54d8868e38c72be9a0 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: executor.h,v 1.92 2003/05/05 17:57:47 tgl Exp $
+ * $Id: executor.h,v 1.93 2003/05/06 00:20:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -82,7 +82,7 @@ extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot);
 /*
  * prototypes from functions in execMain.c
  */
-extern void ExecutorStart(QueryDesc *queryDesc);
+extern void ExecutorStart(QueryDesc *queryDesc, bool explainOnly);
 extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc,
 			ScanDirection direction, long count);
 extern void ExecutorEnd(QueryDesc *queryDesc);
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 2ca16b63272e16030a518996abda1707798dba71..9db779d8bf939358d34579dde781b8b077c6abc5 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: plannodes.h,v 1.64 2003/02/09 00:30:40 tgl Exp $
+ * $Id: plannodes.h,v 1.65 2003/05/06 00:20:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -278,7 +278,9 @@ typedef struct Material
 typedef struct Sort
 {
 	Plan		plan;
-	int			keycount;
+	int			numCols;		/* number of sort-key columns */
+	AttrNumber *sortColIdx;		/* their indexes in the target list */
+	Oid		   *sortOperators;	/* OIDs of operators to sort them by */
 } Sort;
 
 /* ---------------
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 35e2ab26278719f619914a8411cb20d3d93e7cc8..558621c900610d1a870318c3d7182cf3ee316219 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: primnodes.h,v 1.81 2003/04/08 23:20:04 tgl Exp $
+ * $Id: primnodes.h,v 1.82 2003/05/06 00:20:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,7 +30,16 @@
  * Resdom (Result Domain)
  *
  * Notes:
- * ressortgroupref is the parse/plan-time representation of ORDER BY and
+ *
+ * resno will normally be equal to the item's position in a targetlist,
+ * but the code generally tries to avoid relying on that (eg, we avoid
+ * using "nth()" rather than a search to find an item by resno).
+ *
+ * resname will be null if no name can easily be assigned to the column.
+ * But it should never be null for user-visible columns (i.e., non-junk
+ * columns in a toplevel targetlist).
+ *
+ * ressortgroupref is used in the representation of ORDER BY and
  * GROUP BY items.	Targetlist entries with ressortgroupref=0 are not
  * sort/group items.  If ressortgroupref>0, then this item is an ORDER BY or
  * GROUP BY value.	No two entries in a targetlist may have the same nonzero
@@ -39,27 +48,25 @@
  * ressortgroupref means a more significant sort key.)	The order of the
  * associated SortClause or GroupClause lists determine the semantics.
  *
- * reskey and reskeyop are the execution-time representation of sorting.
- * reskey must be zero in any non-sort-key item.  The reskey of sort key
- * targetlist items for a sort plan node is 1,2,...,n for the n sort keys.
- * The reskeyop of each such targetlist item is the sort operator's OID.
- * reskeyop will be zero in non-sort-key items.
+ * resorigtbl/resorigcol identify the source of the column, if it is a
+ * simple reference to a column of a base table (or view).  If it is not
+ * a simple reference, these fields are zeroes.
  *
- * Both reskey and reskeyop are typically zero during parse/plan stages.
- * The executor does not pay any attention to ressortgroupref.
+ * If resjunk is true then the column is a working column (such as a sort key)
+ * that should be removed from the final output of the query.
  *--------------------
  */
 typedef struct Resdom
 {
 	NodeTag		type;
-	AttrNumber	resno;			/* attribute number */
+	AttrNumber	resno;			/* attribute number (1..N) */
 	Oid			restype;		/* type of the value */
 	int32		restypmod;		/* type-specific modifier of the value */
-	char	   *resname;		/* name of the resdom (could be NULL) */
-	Index		ressortgroupref;
-	/* nonzero if referenced by a sort/group clause */
-	Index		reskey;			/* order of key in a sort (for those > 0) */
-	Oid			reskeyop;		/* sort operator's Oid */
+	char	   *resname;		/* name of the column (could be NULL) */
+	Index		ressortgroupref;	/* nonzero if referenced by a
+									 * sort/group clause */
+	Oid			resorigtbl;		/* OID of column's source table */
+	AttrNumber	resorigcol;		/* column's number in source table */
 	bool		resjunk;		/* set to true to eliminate the attribute
 								 * from final target list */
 } Resdom;
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index bd1d757e6a7d90f8e061832cc41c76c99eed380d..35a85e311abf325185210153c7283bea5b116964 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: planmain.h,v 1.69 2003/03/10 03:53:52 tgl Exp $
+ * $Id: planmain.h,v 1.70 2003/05/06 00:20:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,10 +30,10 @@ extern Plan *create_plan(Query *root, Path *best_path);
 extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
 				  Index scanrelid, Plan *subplan);
 extern Append *make_append(List *appendplans, bool isTarget, List *tlist);
-extern Sort *make_sort(Query *root, List *tlist,
-		  Plan *lefttree, int keycount);
 extern Sort *make_sort_from_sortclauses(Query *root, List *tlist,
 										Plan *lefttree, List *sortcls);
+extern Sort *make_sort_from_groupcols(Query *root, List *groupcls,
+									  AttrNumber *grpColIdx, Plan *lefttree);
 extern Agg *make_agg(Query *root, List *tlist, List *qual,
 					 AggStrategy aggstrategy,
 					 int numGroupCols, AttrNumber *grpColIdx,
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index bce5db6c55cf1a378ea81f40de1b9a8414b7ba86..e2afc3ac82192fbb2074bb78e14628039aaef964 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tlist.h,v 1.34 2003/02/15 20:12:41 tgl Exp $
+ * $Id: tlist.h,v 1.35 2003/05/06 00:20:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,7 +22,6 @@ extern Resdom *tlist_member(Node *node, List *targetlist);
 extern void add_var_to_tlist(RelOptInfo *rel, Var *var);
 extern TargetEntry *create_tl_element(Var *var, int resdomno);
 
-extern List *new_unsorted_tlist(List *targetlist);
 extern List *flatten_tlist(List *tlist);
 extern List *add_to_flat_tlist(List *tlist, List *vars);
 
diff --git a/src/include/parser/parse_target.h b/src/include/parser/parse_target.h
index b89ed3a30d6643fcd7f2c9c52358aaf27fa407dd..880673800ae54d2a15c290c4a35469081af15912 100644
--- a/src/include/parser/parse_target.h
+++ b/src/include/parser/parse_target.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_target.h,v 1.29 2003/02/13 05:53:46 momjian Exp $
+ * $Id: parse_target.h,v 1.30 2003/05/06 00:20:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,6 +18,7 @@
 
 
 extern List *transformTargetList(ParseState *pstate, List *targetlist);
+extern void markTargetListOrigins(ParseState *pstate, List *targetlist);
 extern TargetEntry *transformTargetEntry(ParseState *pstate,
 					 Node *node, Node *expr,
 					 char *colname, bool resjunk);
diff --git a/src/include/tcop/dest.h b/src/include/tcop/dest.h
index 5fbe9d33afebd39d890af3952da8dc8637d18521..be2338b7f34e2557e211f106819e8ddfdc76d783 100644
--- a/src/include/tcop/dest.h
+++ b/src/include/tcop/dest.h
@@ -44,7 +44,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: dest.h,v 1.35 2003/05/05 00:44:56 tgl Exp $
+ * $Id: dest.h,v 1.36 2003/05/06 00:20:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -92,10 +92,12 @@ struct _DestReceiver
 {
 	/* Called for each tuple to be output: */
 	void		(*receiveTuple) (HeapTuple tuple, TupleDesc typeinfo,
-											 DestReceiver *self);
+								 DestReceiver *self);
 	/* Initialization and teardown: */
 	void		(*setup) (DestReceiver *self, int operation,
-							 const char *portalName, TupleDesc typeinfo);
+						  const char *portalName,
+						  TupleDesc typeinfo,
+						  List *targetlist);
 	void		(*cleanup) (DestReceiver *self);
 	/* Private fields might appear beyond this point... */
 };