diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index fc95d2ce5bc7042b06fb9fefc493bd3e93848348..b600c0e3a8f1cdbc2713d194ee298a63cf4f3ad2 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -23,6 +23,7 @@
 #include "executor/executor.h"
 #include "executor/nodeAgg.h"
 #include "optimizer/clauses.h"
+#include "optimizer/planmain.h"
 #include "parser/parse_type.h"
 #include "utils/syscache.h"
 
@@ -91,7 +92,7 @@ ExecAgg(Agg *node)
 	EState	   *estate;
 	Plan	   *outerPlan;
 	int			aggno,
-				nagg;
+				numaggs;
 	Datum	   *value1,
 			   *value2;
 	int		   *noInitValue;
@@ -128,19 +129,19 @@ ExecAgg(Agg *node)
 		estate = node->plan.state;
 		econtext = aggstate->csstate.cstate.cs_ExprContext;
 
-		nagg = length(node->aggs);
+		numaggs = length(aggstate->aggs);
 
 		value1 = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_values;
 		nulls = node->aggstate->csstate.cstate.cs_ExprContext->ecxt_nulls;
 
-		value2 = (Datum *) palloc(sizeof(Datum) * nagg);
-		MemSet(value2, 0, sizeof(Datum) * nagg);
+		value2 = (Datum *) palloc(sizeof(Datum) * numaggs);
+		MemSet(value2, 0, sizeof(Datum) * numaggs);
 
-		aggFuncInfo = (AggFuncInfo *) palloc(sizeof(AggFuncInfo) * nagg);
-		MemSet(aggFuncInfo, 0, sizeof(AggFuncInfo) * nagg);
+		aggFuncInfo = (AggFuncInfo *) palloc(sizeof(AggFuncInfo) * numaggs);
+		MemSet(aggFuncInfo, 0, sizeof(AggFuncInfo) * numaggs);
 
-		noInitValue = (int *) palloc(sizeof(int) * nagg);
-		MemSet(noInitValue, 0, sizeof(int) * nagg);
+		noInitValue = (int *) palloc(sizeof(int) * numaggs);
+		MemSet(noInitValue, 0, sizeof(int) * numaggs);
 
 		outerPlan = outerPlan(node);
 		oneTuple = NULL;
@@ -148,7 +149,7 @@ ExecAgg(Agg *node)
 		projInfo = aggstate->csstate.cstate.cs_ProjInfo;
 
 		aggno = -1;
-		foreach(alist, node->aggs)
+		foreach(alist, aggstate->aggs)
 		{
 			Aggref	   *aggref = lfirst(alist);
 			char	   *aggname;
@@ -269,7 +270,7 @@ ExecAgg(Agg *node)
 			}
 
 			aggno = -1;
-			foreach(alist, node->aggs)
+			foreach(alist, aggstate->aggs)
 			{
 				Aggref	   *aggref = lfirst(alist);
 				AggFuncInfo *aggfns = &aggFuncInfo[++aggno];
@@ -367,7 +368,7 @@ ExecAgg(Agg *node)
 		 */
 
 		aggno = -1;
-		foreach(alist, node->aggs)
+		foreach(alist, aggstate->aggs)
 		{
 			char	   *args[2];
 			AggFuncInfo *aggfns = &aggFuncInfo[++aggno];
@@ -467,6 +468,7 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
 	AggState   *aggstate;
 	Plan	   *outerPlan;
 	ExprContext *econtext;
+	int			numaggs;
 
 	/*
 	 * assign the node's execution state
@@ -478,7 +480,16 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
 	 */
 	aggstate = makeNode(AggState);
 	node->aggstate = aggstate;
-	aggstate->agg_done = FALSE;
+	aggstate->agg_done = false;
+
+	/*
+	 * find aggregates in targetlist and quals
+	 */
+	aggstate->aggs = nconc(pull_agg_clause((Node *) node->plan.targetlist),
+						   pull_agg_clause((Node *) node->plan.qual));
+	numaggs = length(aggstate->aggs);
+	if (numaggs <= 0)
+		elog(ERROR, "ExecInitAgg: could not find any aggregate functions");
 
 	/*
 	 * assign node's base id and create expression context
@@ -495,10 +506,10 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
 	ExecInitResultTupleSlot(estate, &aggstate->csstate.cstate);
 
 	econtext = aggstate->csstate.cstate.cs_ExprContext;
-	econtext->ecxt_values = (Datum *) palloc(sizeof(Datum) * length(node->aggs));
-	MemSet(econtext->ecxt_values, 0, sizeof(Datum) * length(node->aggs));
-	econtext->ecxt_nulls = (char *) palloc(sizeof(char) * length(node->aggs));
-	MemSet(econtext->ecxt_nulls, 0, sizeof(char) * length(node->aggs));
+	econtext->ecxt_values = (Datum *) palloc(sizeof(Datum) * numaggs);
+	MemSet(econtext->ecxt_values, 0, sizeof(Datum) * numaggs);
+	econtext->ecxt_nulls = (char *) palloc(sizeof(char) * numaggs);
+	MemSet(econtext->ecxt_nulls, 0, sizeof(char) * numaggs);
 
 	/*
 	 * initializes child nodes
@@ -510,7 +521,7 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
 	 * Result runs in its own context, but make it use our aggregates fix
 	 * for 'select sum(2+2)'
 	 */
-	if (nodeTag(outerPlan) == T_Result)
+	if (IsA(outerPlan, Result))
 	{
 		((Result *) outerPlan)->resstate->cstate.cs_ProjInfo->pi_exprContext->ecxt_values =
 			econtext->ecxt_values;
@@ -645,9 +656,9 @@ ExecReScanAgg(Agg *node, ExprContext *exprCtxt, Plan *parent)
 	AggState   *aggstate = node->aggstate;
 	ExprContext *econtext = aggstate->csstate.cstate.cs_ExprContext;
 
-	aggstate->agg_done = FALSE;
-	MemSet(econtext->ecxt_values, 0, sizeof(Datum) * length(node->aggs));
-	MemSet(econtext->ecxt_nulls, 0, sizeof(char) * length(node->aggs));
+	aggstate->agg_done = false;
+	MemSet(econtext->ecxt_values, 0, sizeof(Datum) * length(aggstate->aggs));
+	MemSet(econtext->ecxt_nulls, 0, sizeof(char) * length(aggstate->aggs));
 
 	/*
 	 * if chgParam of subnode is not null then plan will be re-scanned by
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 99b1c39c7e6e3e00bc4e412a4fdf25e0f8250e5f..4895be7237925bd9bb940a7702bd7b81e1cf997f 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.91 1999/08/16 02:17:41 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.92 1999/08/21 03:48:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -456,12 +456,6 @@ _copyAgg(Agg *from)
 
 	CopyPlanFields((Plan *) from, (Plan *) newnode);
 
-	/*
-	 * Cannot copy agg list; it must be rebuilt to point to subnodes of
-	 * new node.
-	 */
-	set_agg_tlist_references(newnode);
-
 	return newnode;
 }
 
@@ -474,8 +468,8 @@ _copyGroupClause(GroupClause *from)
 {
 	GroupClause *newnode = makeNode(GroupClause);
 
-	newnode->grpOpoid = from->grpOpoid;
-	newnode->tleGroupref = from->tleGroupref;
+	newnode->tleSortGroupRef = from->tleSortGroupRef;
+	newnode->sortop = from->sortop;
 
 	return newnode;
 }
@@ -567,12 +561,11 @@ _copyResdom(Resdom *from)
 	newnode->resno = from->resno;
 	newnode->restype = from->restype;
 	newnode->restypmod = from->restypmod;
-
 	if (from->resname != NULL)
 		newnode->resname = pstrdup(from->resname);
+	newnode->ressortgroupref = from->ressortgroupref;
 	newnode->reskey = from->reskey;
 	newnode->reskeyop = from->reskeyop;
-	newnode->resgroupref = from->resgroupref;
 	newnode->resjunk = from->resjunk;
 
 	return newnode;
@@ -862,8 +855,8 @@ _copyAggref(Aggref *from)
 	newnode->basetype = from->basetype;
 	newnode->aggtype = from->aggtype;
 	Node_Copy(from, newnode, target);
-	newnode->aggno = from->aggno;
 	newnode->usenulls = from->usenulls;
+	newnode->aggno = from->aggno; /* probably not needed */
 
 	return newnode;
 }
@@ -1345,8 +1338,8 @@ _copySortClause(SortClause *from)
 {
 	SortClause *newnode = makeNode(SortClause);
 
-	Node_Copy(from, newnode, resdom);
-	newnode->opoid = from->opoid;
+	newnode->tleSortGroupRef = from->tleSortGroupRef;
+	newnode->sortop = from->sortop;
 
 	return newnode;
 }
@@ -1398,33 +1391,29 @@ _copyQuery(Query *from)
 	newnode->isBinary = from->isBinary;
 	newnode->isTemp = from->isTemp;
 	newnode->unionall = from->unionall;
-	if (from->uniqueFlag)
-		newnode->uniqueFlag = pstrdup(from->uniqueFlag);
-	Node_Copy(from, newnode, sortClause);
+	newnode->hasAggs = from->hasAggs;
+	newnode->hasSubLinks = from->hasSubLinks;
+
 	Node_Copy(from, newnode, rtable);
 	Node_Copy(from, newnode, targetList);
 	Node_Copy(from, newnode, qual);
+	Node_Copy(from, newnode, rowMark);
 
+	if (from->uniqueFlag)
+		newnode->uniqueFlag = pstrdup(from->uniqueFlag);
+	Node_Copy(from, newnode, sortClause);
 	Node_Copy(from, newnode, groupClause);
 	Node_Copy(from, newnode, havingQual);
 
-	newnode->hasAggs = from->hasAggs;
-	newnode->hasSubLinks = from->hasSubLinks;
-
-	if (from->unionClause)
-	{
-		List	   *ulist,
-				   *temp_list = NIL;
-
-		foreach(ulist, from->unionClause)
-			temp_list = lappend(temp_list, copyObject(lfirst(ulist)));
-		newnode->unionClause = temp_list;
-	}
+	/* why is intersectClause missing? */
+	Node_Copy(from, newnode, unionClause);
 
 	Node_Copy(from, newnode, limitOffset);
 	Node_Copy(from, newnode, limitCount);
 
-	Node_Copy(from, newnode, rowMark);
+	/* we do not copy the planner internal fields: base_rel_list,
+	 * join_rel_list, query_pathkeys.  Not entirely clear if this is right?
+	 */
 
 	return newnode;
 }
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 1c53fe6aede6ce11ad519bb9f40339ab0e67da1a..d8538a45066ed4427cedf1acaab925f628601dff 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.47 1999/08/16 02:17:41 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.48 1999/08/21 03:48:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,14 +34,23 @@ _equalResdom(Resdom *a, Resdom *b)
 		return false;
 	if (a->restypmod != b->restypmod)
 		return false;
-	if (strcmp(a->resname, b->resname) != 0)
+	if (a->resname && b->resname)
+	{
+		if (strcmp(a->resname, b->resname) != 0)
+			return false;
+	}
+	else
+	{
+		/* must both be null to be equal */
+		if (a->resname != b->resname)
+			return false;
+	}
+	if (a->ressortgroupref != b->ressortgroupref)
 		return false;
 	if (a->reskey != b->reskey)
 		return false;
 	if (a->reskeyop != b->reskeyop)
 		return false;
-	if (a->resgroupref != b->resgroupref)
-		return false;
 	/* we ignore resjunk flag ... is this correct? */
 
 	return true;
@@ -208,10 +217,9 @@ _equalAggref(Aggref *a, Aggref *b)
 		return false;
 	if (!equal(a->target, b->target))
 		return false;
-	if (a->aggno != b->aggno)
-		return false;
 	if (a->usenulls != b->usenulls)
 		return false;
+	/* ignore aggno, which is only a private field for the executor */
 	return true;
 }
 
@@ -503,6 +511,14 @@ _equalQuery(Query *a, Query *b)
 		return false;
 	if (a->hasSubLinks != b->hasSubLinks)
 		return false;
+	if (!equal(a->rtable, b->rtable))
+		return false;
+	if (!equal(a->targetList, b->targetList))
+		return false;
+	if (!equal(a->qual, b->qual))
+		return false;
+	if (!equal(a->rowMark, b->rowMark))
+		return false;
 	if (a->uniqueFlag && b->uniqueFlag)
 	{
 		if (strcmp(a->uniqueFlag, b->uniqueFlag) != 0)
@@ -515,14 +531,6 @@ _equalQuery(Query *a, Query *b)
 	}
 	if (!equal(a->sortClause, b->sortClause))
 		return false;
-	if (!equal(a->rtable, b->rtable))
-		return false;
-	if (!equal(a->targetList, b->targetList))
-		return false;
-	if (!equal(a->qual, b->qual))
-		return false;
-	if (!equal(a->rowMark, b->rowMark))
-		return false;
 	if (!equal(a->groupClause, b->groupClause))
 		return false;
 	if (!equal(a->havingQual, b->havingQual))
@@ -537,9 +545,9 @@ _equalQuery(Query *a, Query *b)
 		return false;
 
 	/*
-	 * We do not check the internal-to-the-planner fields base_rel_list
-	 * and join_rel_list.  They might not be set yet, and in any case they
-	 * should be derivable from the other fields.
+	 * We do not check the internal-to-the-planner fields: base_rel_list,
+	 * join_rel_list, query_pathkeys.  They might not be set yet, and
+	 * in any case they should be derivable from the other fields.
 	 */
 	return true;
 }
diff --git a/src/backend/nodes/freefuncs.c b/src/backend/nodes/freefuncs.c
index 11f92b28d056c4628e9be84f6fa0157d01fd5c7f..cad49be76bd46a26121c948bf801246fd1648a54 100644
--- a/src/backend/nodes/freefuncs.c
+++ b/src/backend/nodes/freefuncs.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.25 1999/08/16 02:17:42 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.26 1999/08/21 03:48:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -371,8 +371,6 @@ _freeAgg(Agg *node)
 {
 	FreePlanFields((Plan *) node);
 
-	freeList(node->aggs);
-
 	pfree(node);
 }
 
@@ -964,8 +962,6 @@ _freeRowMark(RowMark *node)
 static void
 _freeSortClause(SortClause *node)
 {
-	freeObject(node->resdom);
-
 	pfree(node);
 }
 
@@ -1000,19 +996,22 @@ _freeQuery(Query *node)
 	}
 	if (node->into)
 		pfree(node->into);
+	freeObject(node->rtable);
+	freeObject(node->targetList);
+	freeObject(node->qual);
+	freeObject(node->rowMark);
 	if (node->uniqueFlag)
 		pfree(node->uniqueFlag);
 
 	freeObject(node->sortClause);
-	freeObject(node->rtable);
-	freeObject(node->targetList);
-	freeObject(node->qual);
 	freeObject(node->groupClause);
 	freeObject(node->havingQual);
+	/* why not intersectClause? */
 	freeObject(node->unionClause);
 	freeObject(node->limitOffset);
 	freeObject(node->limitCount);
-	freeObject(node->rowMark);
+
+	/* XXX should we be freeing the planner internal fields? */
 
 	pfree(node);
 }
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index d64fdd8c4628f6049f50001d6130b97c07f21225..26ebed1d458ac9b7b5cdcc17aff9273c0f460c51 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.16 1999/07/15 22:39:17 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/makefuncs.c,v 1.17 1999/08/21 03:48:58 tgl Exp $
  *
  * NOTES
  *	  Creator functions in POSTGRES 4.2 are generated automatically. Most of
@@ -102,9 +102,12 @@ makeResdom(AttrNumber resno,
 	resdom->restype = restype;
 	resdom->restypmod = restypmod;
 	resdom->resname = resname;
+	/* For historical reasons, ressortgroupref defaults to 0 while
+	 * reskey/reskeyop are passed in explicitly.  This is pretty silly.
+	 */
+	resdom->ressortgroupref = 0;
 	resdom->reskey = reskey;
 	resdom->reskeyop = reskeyop;
-	resdom->resgroupref = 0;
 	resdom->resjunk = resjunk;
 	return resdom;
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 39b0625d933e35a2152a13db6e16f34595dd2360..226dbb1cef0d16f709b8080966440a0a05bb6251 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- *	$Id: outfuncs.c,v 1.93 1999/08/16 02:17:42 tgl Exp $
+ *	$Id: outfuncs.c,v 1.94 1999/08/21 03:48:58 tgl Exp $
  *
  * NOTES
  *	  Every (plan) node in POSTGRES has an associated "out" routine which
@@ -237,18 +237,15 @@ _outQuery(StringInfo str, Query *node)
 static void
 _outSortClause(StringInfo str, SortClause *node)
 {
-	appendStringInfo(str, " SORTCLAUSE :resdom ");
-	_outNode(str, node->resdom);
-
-	appendStringInfo(str, " :opoid %u ", node->opoid);
+	appendStringInfo(str, " SORTCLAUSE :tleSortGroupRef %d :sortop %u ",
+					 node->tleSortGroupRef, node->sortop);
 }
 
 static void
 _outGroupClause(StringInfo str, GroupClause *node)
 {
-	appendStringInfo(str, " GROUPCLAUSE :grpOpoid %u :tleGroupref %d",
-					 node->grpOpoid,
-					 node->tleGroupref);
+	appendStringInfo(str, " GROUPCLAUSE :tleSortGroupRef %d :sortop %u ",
+					 node->tleSortGroupRef, node->sortop);
 }
 
 /*
@@ -482,9 +479,6 @@ _outAgg(StringInfo str, Agg *node)
 
 	appendStringInfo(str, " AGG ");
 	_outPlanInfo(str, (Plan *) node);
-
-	appendStringInfo(str, " :aggs ");
-	_outNode(str, node->aggs);
 }
 
 static void
@@ -549,8 +543,8 @@ _outResdom(StringInfo str, Resdom *node)
 					 node->reskey,
 					 node->reskeyop);
 
-	appendStringInfo(str, " :resgroupref %d :resjunk %s ",
-					 node->resgroupref,
+	appendStringInfo(str, " :ressortgroupref %d :resjunk %s ",
+					 node->ressortgroupref,
 					 node->resjunk ? "true" : "false");
 }
 
@@ -665,8 +659,7 @@ _outAggref(StringInfo str, Aggref *node)
 					 node->aggtype);
 	_outNode(str, node->target);
 
-	appendStringInfo(str, ":aggno %d :usenulls %s",
-					 node->aggno,
+	appendStringInfo(str, " :usenulls %s ",
 					 node->usenulls ? "true" : "false");
 }
 
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 98385dd96f68ca57fd1ee356c5a33ea0cdbb2e54..588528daa1d1911664604454eab71ac84dcae2ee 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.72 1999/08/16 02:17:43 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.73 1999/08/21 03:48:58 tgl Exp $
  *
  * NOTES
  *	  Most of the read functions for plan nodes are tested. (In fact, they
@@ -184,12 +184,13 @@ _readSortClause()
 
 	local_node = makeNode(SortClause);
 
-	token = lsptok(NULL, &length);		/* skip the :resdom */
-	local_node->resdom = nodeRead(true);
+	token = lsptok(NULL, &length);		/* skip :tleSortGroupRef */
+	token = lsptok(NULL, &length);		/* get tleSortGroupRef */
+	local_node->tleSortGroupRef = strtoul(token, NULL, 10);
 
-	token = lsptok(NULL, &length);		/* skip :opoid */
-	token = lsptok(NULL, &length);		/* get opoid */
-	local_node->opoid = strtoul(token, NULL, 10);
+	token = lsptok(NULL, &length);		/* skip :sortop */
+	token = lsptok(NULL, &length);		/* get sortop */
+	local_node->sortop = strtoul(token, NULL, 10);
 
 	return local_node;
 }
@@ -207,13 +208,13 @@ _readGroupClause()
 
 	local_node = makeNode(GroupClause);
 
-	token = lsptok(NULL, &length);		/* skip :grpOpoid */
-	token = lsptok(NULL, &length);		/* get grpOpoid */
-	local_node->grpOpoid = strtoul(token, NULL, 10);
+	token = lsptok(NULL, &length);		/* skip :tleSortGroupRef */
+	token = lsptok(NULL, &length);		/* get tleSortGroupRef */
+	local_node->tleSortGroupRef = strtoul(token, NULL, 10);
 
-	token = lsptok(NULL, &length);		/* skip :tleGroupref */
-	token = lsptok(NULL, &length);		/* get tleGroupref */
-	local_node->tleGroupref = strtoul(token, NULL, 10);
+	token = lsptok(NULL, &length);		/* skip :sortop */
+	token = lsptok(NULL, &length);		/* get sortop */
+	local_node->sortop = strtoul(token, NULL, 10);
 
 	return local_node;
 }
@@ -600,15 +601,10 @@ static Agg *
 _readAgg()
 {
 	Agg		   *local_node;
-	char	   *token;
-	int			length;
 
 	local_node = makeNode(Agg);
 	_getPlan((Plan *) local_node);
 
-	token = lsptok(NULL, &length);		/* eat :agg */
-	local_node->aggs = nodeRead(true);	/* now read it */
-
 	return local_node;
 }
 
@@ -712,9 +708,9 @@ _readResdom()
 	token = lsptok(NULL, &length);		/* get reskeyop */
 	local_node->reskeyop = (Oid) atol(token);
 
-	token = lsptok(NULL, &length);		/* eat :resgroupref */
-	token = lsptok(NULL, &length);		/* get resgroupref */
-	local_node->resgroupref = strtoul(token, NULL, 10);
+	token = lsptok(NULL, &length);		/* eat :ressortgroupref */
+	token = lsptok(NULL, &length);		/* get ressortgroupref */
+	local_node->ressortgroupref = strtoul(token, NULL, 10);
 
 	token = lsptok(NULL, &length);		/* eat :resjunk */
 	token = lsptok(NULL, &length);		/* get resjunk */
@@ -1163,10 +1159,6 @@ _readAggref()
 	token = lsptok(NULL, &length);		/* eat :target */
 	local_node->target = nodeRead(true);		/* now read it */
 
-	token = lsptok(NULL, &length);		/* eat :aggno */
-	token = lsptok(NULL, &length);		/* get aggno */
-	local_node->aggno = atoi(token);
-
 	token = lsptok(NULL, &length);		/* eat :usenulls */
 	token = lsptok(NULL, &length);		/* get usenulls */
 	local_node->usenulls = (token[0] == 't') ? true : false;
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 99ce241fed1003e236dc8ab211e42a0cdc8a430e..6af480629e282d57df243f12624e972c28fcbac4 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.69 1999/08/16 02:17:50 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.70 1999/08/21 03:49:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -70,6 +70,8 @@ static List *index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
 							 List *clausegroup_list, List *outerrelids_list);
 static bool useful_for_mergejoin(RelOptInfo *rel, RelOptInfo *index,
 								 List *joininfo_list);
+static bool useful_for_ordering(Query *root, RelOptInfo *rel,
+								RelOptInfo *index);
 static bool match_index_to_operand(int indexkey, Var *operand,
 								   RelOptInfo *rel, RelOptInfo *index);
 static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index);
@@ -86,7 +88,8 @@ static List *prefix_quals(Var *leftop, Oid expr_op,
  *	  Generate all interesting index paths for the given relation.
  *
  * To be considered for an index scan, an index must match one or more
- * restriction clauses or join clauses from the query's qual condition.
+ * restriction clauses or join clauses from the query's qual condition,
+ * or match the query's ORDER BY condition.
  *
  * There are two basic kinds of index scans.  A "plain" index scan uses
  * only restriction clauses (possibly none at all) in its indexqual,
@@ -104,14 +107,6 @@ static List *prefix_quals(Var *leftop, Oid expr_op,
  * relations.  The innerjoin paths are *not* in the return list, but are
  * appended to the "innerjoin" list of the relation itself.
  *
- * XXX An index scan might also be used simply to order the result.  We
- * probably should create an index path for any index that matches the
- * query's ORDER BY condition, even if it doesn't seem useful for join
- * or restriction clauses.  But currently, such a path would never
- * survive the path selection process, so there's no point.  The selection
- * process needs to award bonus scores to indexscans that produce a
- * suitably-ordered result...
- *
  * 'rel' is the relation for which we want to generate index paths
  * 'indices' is a list of available indexes for 'rel'
  * 'restrictinfo_list' is a list of restrictinfo nodes for 'rel'
@@ -192,13 +187,16 @@ create_index_paths(Query *root,
 		 * index path for it even if there were no restriction clauses.
 		 * (If there were, there is no need to make another index path.)
 		 * This will allow the index to be considered as a base for a
-		 * mergejoin in later processing.
+		 * mergejoin in later processing.  Similarly, if the index matches
+		 * the ordering that is needed for the overall query result, make
+		 * an index path for it even if there is no other reason to do so.
 		 */
-		if (restrictclauses == NIL &&
-			useful_for_mergejoin(rel, index, joininfo_list))
+		if (restrictclauses == NIL)
 		{
-			retval = lappend(retval,
-							 create_index_path(root, rel, index, NIL));
+			if (useful_for_mergejoin(rel, index, joininfo_list) ||
+				useful_for_ordering(root, rel, index))
+				retval = lappend(retval,
+								 create_index_path(root, rel, index, NIL));
 		}
 
 		/*
@@ -748,6 +746,101 @@ indexable_operator(Expr *clause, int xclass, Oid relam,
 	return false;
 }
 
+/*
+ * useful_for_mergejoin
+ *	  Determine whether the given index can support a mergejoin based
+ *	  on any available join clause.
+ *
+ *	  We look to see whether the first indexkey of the index matches the
+ *	  left or right sides of any of the mergejoinable clauses and provides
+ *	  the ordering needed for that side.  If so, the index is useful.
+ *	  Matching a second or later indexkey is not useful unless there is
+ *	  also a mergeclause for the first indexkey, so we need not consider
+ *	  secondary indexkeys at this stage.
+ *
+ * 'rel' is the relation for which 'index' is defined
+ * 'joininfo_list' is the list of JoinInfo nodes for 'rel'
+ */
+static bool
+useful_for_mergejoin(RelOptInfo *rel,
+					 RelOptInfo *index,
+					 List *joininfo_list)
+{
+	int		   *indexkeys = index->indexkeys;
+	Oid		   *ordering = index->ordering;
+	List	   *i;
+
+	if (!indexkeys || indexkeys[0] == 0 ||
+		!ordering || ordering[0] == InvalidOid)
+		return false;			/* unordered index is not useful */
+
+	foreach(i, joininfo_list)
+	{
+		JoinInfo   *joininfo = (JoinInfo *) lfirst(i);
+		List	   *j;
+
+		foreach(j, joininfo->jinfo_restrictinfo)
+		{
+			RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j);
+
+			if (restrictinfo->mergejoinoperator)
+			{
+				if (restrictinfo->left_sortop == ordering[0] &&
+					match_index_to_operand(indexkeys[0],
+										   get_leftop(restrictinfo->clause),
+										   rel, index))
+					return true;
+				if (restrictinfo->right_sortop == ordering[0] &&
+					match_index_to_operand(indexkeys[0],
+										   get_rightop(restrictinfo->clause),
+										   rel, index))
+					return true;
+			}
+		}
+	}
+	return false;
+}
+
+/*
+ * useful_for_ordering
+ *	  Determine whether the given index can produce an ordering matching
+ *	  the order that is wanted for the query result.
+ *
+ * We check to see whether either forward or backward scan direction can
+ * match the specified pathkeys.
+ *
+ * 'rel' is the relation for which 'index' is defined
+ */
+static bool
+useful_for_ordering(Query *root,
+					RelOptInfo *rel,
+					RelOptInfo *index)
+{
+	List	   *index_pathkeys;
+
+	if (root->query_pathkeys == NIL)
+		return false;			/* no special ordering requested */
+
+	index_pathkeys = build_index_pathkeys(root, rel, index);
+
+	if (index_pathkeys == NIL)
+		return false;			/* unordered index */
+
+	if (pathkeys_contained_in(root->query_pathkeys, index_pathkeys))
+		return true;
+
+	/* caution: commute_pathkeys destructively modifies its argument;
+	 * safe because we just built the index_pathkeys for local use here.
+	 */
+	if (commute_pathkeys(index_pathkeys))
+	{
+		if (pathkeys_contained_in(root->query_pathkeys, index_pathkeys))
+			return true;		/* useful as a reverse-order path */
+	}
+
+	return false;
+}
+
 /****************************************************************************
  *				----  ROUTINES TO DO PARTIAL INDEX PREDICATE TESTS	----
  ****************************************************************************/
@@ -1285,61 +1378,6 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
 	return path_list;
 }
 
-/*
- * useful_for_mergejoin
- *	  Determine whether the given index can support a mergejoin based
- *	  on any available join clause.
- *
- *	  We look to see whether the first indexkey of the index matches the
- *	  left or right sides of any of the mergejoinable clauses and provides
- *	  the ordering needed for that side.  If so, the index is useful.
- *	  Matching a second or later indexkey is not useful unless there is
- *	  also a mergeclause for the first indexkey, so we need not consider
- *	  secondary indexkeys at this stage.
- *
- * 'rel' is the relation for which 'index' is defined
- * 'joininfo_list' is the list of JoinInfo nodes for 'rel'
- */
-static bool
-useful_for_mergejoin(RelOptInfo *rel,
-					 RelOptInfo *index,
-					 List *joininfo_list)
-{
-	int		   *indexkeys = index->indexkeys;
-	Oid		   *ordering = index->ordering;
-	List	   *i;
-
-	if (!indexkeys || indexkeys[0] == 0 ||
-		!ordering || ordering[0] == InvalidOid)
-		return false;			/* unordered index is not useful */
-
-	foreach(i, joininfo_list)
-	{
-		JoinInfo   *joininfo = (JoinInfo *) lfirst(i);
-		List	   *j;
-
-		foreach(j, joininfo->jinfo_restrictinfo)
-		{
-			RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j);
-
-			if (restrictinfo->mergejoinoperator)
-			{
-				if (restrictinfo->left_sortop == ordering[0] &&
-					match_index_to_operand(indexkeys[0],
-										   get_leftop(restrictinfo->clause),
-										   rel, index))
-					return true;
-				if (restrictinfo->right_sortop == ordering[0] &&
-					match_index_to_operand(indexkeys[0],
-										   get_rightop(restrictinfo->clause),
-										   rel, index))
-					return true;
-			}
-		}
-	}
-	return false;
-}
-
 /****************************************************************************
  *				----  ROUTINES TO CHECK OPERANDS  ----
  ****************************************************************************/
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index 55891d87a95ffe8865dc63d9f2be9811eff711ad..c1ac6a2c4d8272c7a7eb59be08bcc409dd7b5651 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.45 1999/08/16 02:17:51 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.46 1999/08/21 03:49:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -408,7 +408,8 @@ match_unsorted_outer(RelOptInfo *joinrel,
 				trialinnerpath =
 					get_cheapest_path_for_pathkeys(innerrel->pathlist,
 												   ltruncate(clausecount,
-															 trialsortkeys));
+															 trialsortkeys),
+												   false);
 				if (trialinnerpath != NULL &&
 					trialinnerpath->path_cost < cheapest_cost)
 				{
@@ -488,7 +489,8 @@ match_unsorted_inner(RelOptInfo *joinrel,
 			/* Look for an outer path already ordered well enough to merge */
 			mergeouterpath =
 				get_cheapest_path_for_pathkeys(outerrel->pathlist,
-											   outersortkeys);
+											   outersortkeys,
+											   false);
 
 			/* Should we use the mergeouter, or sort the cheapest outer? */
 			if (mergeouterpath != NULL &&
diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c
index 44a7b614b6942417e05c5da34dec71652195f35d..41a3ff35b4813a6112d08762966ae9fe2000ee54 100644
--- a/src/backend/optimizer/path/pathkeys.c
+++ b/src/backend/optimizer/path/pathkeys.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.14 1999/08/16 02:17:52 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.15 1999/08/21 03:49:01 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,6 +20,7 @@
 #include "optimizer/tlist.h"
 #include "optimizer/var.h"
 #include "parser/parsetree.h"
+#include "parser/parse_func.h"
 #include "utils/lsyscache.h"
 
 static PathKeyItem *makePathKeyItem(Node *key, Oid sortop);
@@ -89,6 +90,11 @@ static List *build_join_pathkey(List *pathkeys, List *join_rel_tlist,
  *	executor might have to split the join into multiple batches.  Therefore
  *	a Hashjoin is always given NIL pathkeys.
  *
+ *	Pathkeys are also useful to represent an ordering that we wish to achieve,
+ *	since they are easily compared to the pathkeys of a potential candidate
+ *	path.  So, SortClause lists are turned into pathkeys lists for use inside
+ *	the optimizer.
+ *
  *	-- bjm & tgl
  *--------------------
  */
@@ -254,9 +260,11 @@ pathkeys_contained_in(List *keys1, List *keys2)
  *
  * 'paths' is a list of possible paths (either inner or outer)
  * 'pathkeys' represents a required ordering
+ * if 'indexpaths_only' is true, only IndexPaths will be considered.
  */
 Path *
-get_cheapest_path_for_pathkeys(List *paths, List *pathkeys)
+get_cheapest_path_for_pathkeys(List *paths, List *pathkeys,
+							   bool indexpaths_only)
 {
 	Path	   *matched_path = NULL;
 	List	   *i;
@@ -265,6 +273,9 @@ get_cheapest_path_for_pathkeys(List *paths, List *pathkeys)
 	{
 		Path	   *path = (Path *) lfirst(i);
 
+		if (indexpaths_only && ! IsA(path, IndexPath))
+			continue;
+
 		if (pathkeys_contained_in(pathkeys, path->pathkeys))
 		{
 			if (matched_path == NULL ||
@@ -314,7 +325,8 @@ build_index_pathkeys(Query *root, RelOptInfo *rel, RelOptInfo *index)
 		funcnode->funcisindex = false;
 		funcnode->funcsize = 0;
 		funcnode->func_fcache = NULL;
-		funcnode->func_tlist = NIL;
+		/* we assume here that the function returns a base type... */
+		funcnode->func_tlist = setup_base_tlist(funcnode->functype);
 		funcnode->func_planlist = NIL;
 
 		while (*indexkeys != 0)
@@ -516,6 +528,70 @@ build_join_pathkey(List *pathkey,
 	return new_pathkey;
 }
 
+/*
+ * commute_pathkeys
+ *		Attempt to commute the operators in a set of pathkeys, producing
+ *		pathkeys that describe the reverse sort order (DESC instead of ASC).
+ *		Returns TRUE if successful (all the operators have commutators).
+ *
+ * CAUTION: given pathkeys are modified in place, even if not successful!!
+ * Usually, caller should have just built or copied the pathkeys list to
+ * ensure there are no unwanted side-effects.
+ */
+bool
+commute_pathkeys(List *pathkeys)
+{
+	List	   *i;
+
+	foreach(i, pathkeys)
+	{
+		List	   *pathkey = lfirst(i);
+		List	   *j;
+
+		foreach(j, pathkey)
+		{
+			PathKeyItem	   *key = lfirst(j);
+
+			key->sortop = get_commutator(key->sortop);
+			if (key->sortop == InvalidOid)
+				return false;
+		}
+	}
+	return true;				/* successful */
+}
+
+/****************************************************************************
+ *		PATHKEYS AND SORT CLAUSES
+ ****************************************************************************/
+
+/*
+ * make_pathkeys_for_sortclauses
+ *		Generate a pathkeys list that represents the sort order specified
+ *		by a list of SortClauses (GroupClauses will work too!)
+ *
+ * 'sortclauses' is a list of SortClause or GroupClause nodes
+ * 'tlist' is the targetlist to find the referenced tlist entries in
+ */
+List *
+make_pathkeys_for_sortclauses(List *sortclauses, List *tlist)
+{
+	List	   *pathkeys = NIL;
+	List	   *i;
+
+	foreach(i, sortclauses)
+	{
+		SortClause	   *sortcl = (SortClause *) lfirst(i);
+		Node		   *sortkey;
+		PathKeyItem	   *pathkey;
+
+		sortkey = get_sortgroupclause_expr(sortcl, tlist);
+		pathkey = makePathKeyItem(sortkey, sortcl->sortop);
+		/* pathkey becomes a one-element sublist */
+		pathkeys = lappend(pathkeys, lcons(pathkey, NIL));
+	}
+	return pathkeys;
+}
+
 /****************************************************************************
  *		PATHKEYS AND MERGECLAUSES
  ****************************************************************************/
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 40923fe27dadc731cabaaa8f10bddf5f866d982b..d1f756fc7c19fc44be0f1a15dd3989fab8c485b8 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.73 1999/08/18 04:15:16 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.74 1999/08/21 03:49:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1131,7 +1131,6 @@ make_agg(List *tlist, Plan *lefttree)
 	node->plan.targetlist = tlist;
 	node->plan.lefttree = lefttree;
 	node->plan.righttree = (Plan *) NULL;
-	node->aggs = NIL;
 
 	return node;
 }
@@ -1141,15 +1140,15 @@ make_group(List *tlist,
 		   bool tuplePerGroup,
 		   int ngrp,
 		   AttrNumber *grpColIdx,
-		   Sort *lefttree)
+		   Plan *lefttree)
 {
 	Group	   *node = makeNode(Group);
 
-	copy_costsize(&node->plan, (Plan *) lefttree);
+	copy_costsize(&node->plan, lefttree);
 	node->plan.state = (EState *) NULL;
 	node->plan.qual = NULL;
 	node->plan.targetlist = tlist;
-	node->plan.lefttree = (Plan *) lefttree;
+	node->plan.lefttree = lefttree;
 	node->plan.righttree = (Plan *) NULL;
 	node->tuplePerGroup = tuplePerGroup;
 	node->numCols = ngrp;
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 76e010c9d228c890053ff9775fe381d9bbcef1de..f6f62abfe0841e8d6b05f7bf3e818cc7669e6dc6 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.40 1999/07/16 04:59:19 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.41 1999/08/21 03:49:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,11 +17,13 @@
 
 
 #include "optimizer/clauses.h"
+#include "optimizer/cost.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
 #include "optimizer/prep.h"
 #include "optimizer/subselect.h"
 #include "optimizer/tlist.h"
+#include "utils/lsyscache.h"
 
 
 static Plan *subplanner(Query *root, List *flat_tlist, List *qual);
@@ -42,6 +44,13 @@ static Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
  *	  tlist is the target list of the query
  *	  qual is the qualification of the query
  *
+ *	  Note: the Query node now also includes a query_pathkeys field, which
+ *	  signals query_planner that the indicated sort order is wanted in the
+ *	  final output plan.  If, for some reason, query_planner is unable to
+ *	  comply, it sets query_pathkeys to NIL before returning.  (The reason
+ *	  query_pathkeys is a Query field and not a passed parameter is that
+ *	  the low-level routines in indxpath.c need to see it.)
+ *
  *	  Returns a query plan.
  */
 Plan *
@@ -100,6 +109,8 @@ query_planner(Query *root,
 	 */
 	if (var_only_tlist == NULL && qual == NULL)
 	{
+		root->query_pathkeys = NIL; /* these plans make unordered results */
+
 		switch (command_type)
 		{
 			case CMD_SELECT:
@@ -152,6 +163,8 @@ query_planner(Query *root,
 		 */
 		set_tlist_references(subplan);
 
+		root->query_pathkeys = NIL; /* result is unordered, no? */
+
 		return subplan;
 	}
 
@@ -163,15 +176,15 @@ query_planner(Query *root,
 	 * responsibility to optimally push these expressions down the plan
 	 * tree.  -- Wei
 	 *
-	 * Note: formerly there was a test here to skip the flatten call if we
-	 * expected union_planner to insert a Group or Agg node above our
+	 * Note: formerly there was a test here to skip the unflatten call if
+	 * we expected union_planner to insert a Group or Agg node above our
 	 * result. However, now union_planner tells us exactly what it wants
 	 * returned, and we just do it.  Much cleaner.
 	 */
 	else
 	{
-		subplan->targetlist = flatten_tlist_vars(tlist,
-												 subplan->targetlist);
+		subplan->targetlist = unflatten_tlist(tlist,
+											  subplan->targetlist);
 		return subplan;
 	}
 
@@ -204,6 +217,8 @@ subplanner(Query *root,
 		   List *qual)
 {
 	RelOptInfo *final_rel;
+	Cost		cheapest_cost;
+	Path	   *sortedpath;
 
 	/*
 	 * Initialize the targetlist and qualification, adding entries to
@@ -244,18 +259,83 @@ subplanner(Query *root,
 	}
 #endif
 
+	if (! final_rel)
+	{
+		elog(NOTICE, "final relation is null");
+		root->query_pathkeys = NIL; /* result is unordered, no? */
+		return create_plan((Path *) NULL);
+	}
+
+	/*
+	 * Determine the cheapest path and create a subplan to execute it.
+	 *
+	 * If no special sort order is wanted, or if the cheapest path is
+	 * already appropriately ordered, just use the cheapest path.
+	 */
+	if (root->query_pathkeys == NIL ||
+		pathkeys_contained_in(root->query_pathkeys,
+							  final_rel->cheapestpath->pathkeys))
+		return create_plan(final_rel->cheapestpath);
 	/*
-	 * Determine the cheapest path and create a subplan corresponding to
-	 * it.
+	 * Otherwise, look to see if we have an already-ordered path that is
+	 * cheaper than doing an explicit sort on cheapestpath.
 	 */
-	if (final_rel)
-		return create_plan((Path *) final_rel->cheapestpath);
+	cheapest_cost = final_rel->cheapestpath->path_cost +
+		cost_sort(root->query_pathkeys, final_rel->size, final_rel->width);
+
+	sortedpath = get_cheapest_path_for_pathkeys(final_rel->pathlist,
+												root->query_pathkeys,
+												false);
+	if (sortedpath)
+	{
+		if (sortedpath->path_cost <= cheapest_cost)
+		{
+			/* Found a better presorted path, use it */
+			return create_plan(sortedpath);
+		}
+		/* otherwise, doing it the hard way is still cheaper */
+	}
 	else
 	{
-		elog(NOTICE, "final relation is null");
-		return create_plan((Path *) NULL);
+		/*
+		 * If we found no usable presorted path at all, it is possible
+		 * that the user asked for descending sort order.  Check to see
+		 * if we can satisfy the pathkeys by using a backwards indexscan.
+		 * To do this, we commute all the operators in the pathkeys and
+		 * then look for a matching path that is an IndexPath.
+		 */
+		List	   *commuted_pathkeys = copyObject(root->query_pathkeys);
+
+		if (commute_pathkeys(commuted_pathkeys))
+		{
+			/* pass 'true' to force only IndexPaths to be considered */
+			sortedpath = get_cheapest_path_for_pathkeys(final_rel->pathlist,
+														commuted_pathkeys,
+														true);
+			if (sortedpath && sortedpath->path_cost <= cheapest_cost)
+			{
+				/*
+				 * Kluge here: since IndexPath has no representation for
+				 * backwards scan, we have to convert to Plan format and
+				 * then poke the result.
+				 */
+				Plan	   *sortedplan = create_plan(sortedpath);
+
+				Assert(IsA(sortedplan, IndexScan));
+				((IndexScan *) sortedplan)->indxorderdir = BackwardScanDirection;
+				return sortedplan;
+			}
+		}
 	}
 
+	/* Nothing for it but to sort the cheapestpath...
+	 *
+	 * we indicate we failed to sort the plan, and let the caller
+	 * stick the appropriate sortplan on top.
+	 */
+	root->query_pathkeys = NIL; /* sorry, it ain't sorted */
+
+	return create_plan(final_rel->cheapestpath);
 }
 
 /*****************************************************************************
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 17ad449839b24496fe8efa6dc7b330d02afdc973..b328c40f226714a1ed601a004981c302fb5f5a9f 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.62 1999/08/09 06:20:26 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.63 1999/08/21 03:49:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,6 +22,7 @@
 #include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/internal.h"
+#include "optimizer/paths.h"
 #include "optimizer/planmain.h"
 #include "optimizer/planner.h"
 #include "optimizer/prep.h"
@@ -35,11 +36,10 @@
 #include "utils/syscache.h"
 
 static List *make_subplanTargetList(Query *parse, List *tlist,
-					   AttrNumber **groupColIdx);
+									AttrNumber **groupColIdx);
 static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
-			   List *groupClause, AttrNumber *grpColIdx,
-			   Plan *subplan);
-static ScanDirection get_dir_to_omit_sortplan(List *sortcls, Plan *plan);
+							List *groupClause, AttrNumber *grpColIdx,
+							bool is_sorted, Plan *subplan);
 static Plan *make_sortplan(List *tlist, List *sortcls, Plan *plannode);
 
 /*****************************************************************************
@@ -59,6 +59,7 @@ planner(Query *parse)
 	PlannerPlanId = 0;
 
 	transformKeySetQuery(parse);
+
 	result_plan = union_planner(parse);
 
 	Assert(PlannerQueryLevel == 1);
@@ -88,6 +89,7 @@ union_planner(Query *parse)
 	List	   *rangetable = parse->rtable;
 	Plan	   *result_plan = (Plan *) NULL;
 	AttrNumber *groupColIdx = NULL;
+	bool		is_sorted = false;
 	Index		rt_index;
 
 	if (parse->unionClause)
@@ -179,6 +181,34 @@ union_planner(Query *parse)
 			}
 		}
 
+		/*
+		 * Figure out whether we need a sorted result from query_planner.
+		 *
+		 * If we have a GROUP BY clause, then we want a result sorted
+		 * properly for grouping.  Otherwise, if there is an ORDER BY clause
+		 * and no need for an aggregate node, we want to sort by the ORDER BY
+		 * clause.  (XXX In some cases, we could presort even when there is
+		 * an aggregate, but I'll leave that refinement for another day.)
+		 *
+		 * NOTE: the reason we put the target pathkeys into the Query node
+		 * rather than passing them as an argument to query_planner is that
+		 * the low-level routines in indxpath.c want to be able to see them.
+		 */
+		if (parse->groupClause)
+		{
+			parse->query_pathkeys =
+				make_pathkeys_for_sortclauses(parse->groupClause, tlist);
+		}
+		else if (parse->sortClause && ! parse->hasAggs)
+		{
+			parse->query_pathkeys =
+				make_pathkeys_for_sortclauses(parse->sortClause, tlist);
+		}
+		else
+		{
+			parse->query_pathkeys = NIL;
+		}
+
 		/*
 		 * Generate appropriate target list for subplan; may be different
 		 * from tlist if grouping or aggregation is needed.
@@ -190,6 +220,12 @@ union_planner(Query *parse)
 									parse->commandType,
 									sub_tlist,
 									(List *) parse->qual);
+
+		/* query_planner sets query_pathkeys to NIL if it didn't make
+		 * a properly sorted plan
+		 */
+		if (parse->query_pathkeys)
+			is_sorted = true;
 	}
 
 	/* query_planner returns NULL if it thinks plan is bogus */
@@ -197,8 +233,8 @@ union_planner(Query *parse)
 		elog(ERROR, "union_planner: failed to create plan");
 
 	/*
-	 * If we have a GROUP BY clause, insert a group node (with the
-	 * appropriate sort node.)
+	 * If we have a GROUP BY clause, insert a group node (plus the
+	 * appropriate sort node, if necessary).
 	 */
 	if (parse->groupClause)
 	{
@@ -215,17 +251,27 @@ union_planner(Query *parse)
 
 		/*
 		 * If there are aggregates then the Group node should just return
-		 * the same (simplified) tlist as the subplan, which we indicate
-		 * to make_groupplan by passing NIL.  If there are no aggregates
+		 * the same set of vars as the subplan did (but we can exclude
+		 * any GROUP BY expressions).  If there are no aggregates
 		 * then the Group node had better compute the final tlist.
 		 */
-		group_tlist = parse->hasAggs ? NIL : tlist;
+		if (parse->hasAggs)
+			group_tlist = flatten_tlist(result_plan->targetlist);
+		else
+			group_tlist = tlist;
 
 		result_plan = make_groupplan(group_tlist,
 									 tuplePerGroup,
 									 parse->groupClause,
 									 groupColIdx,
+									 is_sorted,
 									 result_plan);
+
+		/*
+		 * Assume the result of the group step is not ordered suitably
+		 * for any ORDER BY that may exist.  XXX it might be; improve this!
+		 */
+		is_sorted = false;
 	}
 
 	/*
@@ -269,66 +315,48 @@ union_planner(Query *parse)
 		result_plan->qual = (List *) parse->havingQual;
 
 		/*
-		 * Update vars to refer to subplan result tuples, find Aggrefs,
+		 * Update vars to refer to subplan result tuples, and
 		 * make sure there is an Aggref in every HAVING clause.
 		 */
 		if (!set_agg_tlist_references((Agg *) result_plan))
 			elog(ERROR, "SELECT/HAVING requires aggregates to be valid");
 
 		/*
-		 * Check that we actually found some aggregates, else executor
-		 * will die unpleasantly.  (This defends against possible bugs in
-		 * parser or rewrite that might cause hasAggs to be incorrectly
-		 * set 'true'. It's not easy to recover here, since we've already
-		 * made decisions assuming there will be an Agg node.)
+		 * Assume result is not ordered suitably for ORDER BY.
+		 * XXX it might be; improve this!
 		 */
-		if (((Agg *) result_plan)->aggs == NIL)
-			elog(ERROR, "union_planner: query is marked hasAggs, but I don't see any");
+		is_sorted = false;
 	}
 
 	/*
-	 * For now, before we hand back the plan, check to see if there is a
-	 * user-specified sort that needs to be done.  Eventually, this will
-	 * be moved into the guts of the planner s.t. user specified sorts
-	 * will be considered as part of the planning process. Since we can
-	 * only make use of user-specified sorts in special cases, we can do
-	 * the optimization step later.
+	 * If we were not able to make the plan come out in the right order,
+	 * add an explicit sort step.
 	 */
-
-	if (parse->uniqueFlag)
+	if (parse->sortClause && ! is_sorted)
 	{
-		Plan	   *sortplan = make_sortplan(tlist, parse->sortClause, result_plan);
-
-		return ((Plan *) make_unique(tlist, sortplan, parse->uniqueFlag));
+		result_plan = make_sortplan(tlist, parse->sortClause, result_plan);
 	}
-	else
+
+	/*
+	 * Finally, if there is a UNIQUE clause, add the UNIQUE node.
+	 */
+	if (parse->uniqueFlag)
 	{
-		if (parse->sortClause)
-		{
-			ScanDirection	dir = get_dir_to_omit_sortplan(parse->sortClause, result_plan);
-			if (ScanDirectionIsNoMovement(dir))
-				return (make_sortplan(tlist, parse->sortClause, result_plan));
-			else
-			{ 
-				((IndexScan *)result_plan)->indxorderdir = dir;
-				return ((Plan *) result_plan);
-			}
-		}
-		else
-			return ((Plan *) result_plan);
+		result_plan = (Plan *) make_unique(tlist, result_plan,
+										   parse->uniqueFlag);
 	}
 
+	return result_plan;
 }
 
 /*---------------
  * make_subplanTargetList
- *	  Generate appropriate target lists when grouping is required.
+ *	  Generate appropriate target list when grouping is required.
  *
- * When union_planner inserts Aggregate and/or Group/Sort plan nodes above
- * the result of query_planner, we typically need to pass a different
+ * When union_planner inserts Aggregate and/or Group plan nodes above
+ * the result of query_planner, we typically want to pass a different
  * target list to query_planner than the outer plan nodes should have.
- * This routine generates the correct target list for the subplan, and
- * if necessary modifies the target list for the inserted nodes as well.
+ * This routine generates the correct target list for the subplan.
  *
  * The initial target list passed from the parser already contains entries
  * for all ORDER BY and GROUP BY expressions, but it will not have entries
@@ -340,26 +368,18 @@ union_planner(Query *parse)
  * given a query like
  *		SELECT a+b,SUM(c+d) FROM table GROUP BY a+b;
  * we want to pass this targetlist to the subplan:
- *		a+b,c,d
+ *		a,b,c,d,a+b
  * where the a+b target will be used by the Sort/Group steps, and the
- * c and d targets will be needed to compute the aggregate results.
+ * other targets will be used for computing the final results.  (In the
+ * above example we could theoretically suppress the a and b targets and
+ * use only a+b, but it's not really worth the trouble.)
  *
  * 'parse' is the query being processed.
- * 'tlist' is the query's target list.  CAUTION: list elements may be
- * modified by this routine!
+ * 'tlist' is the query's target list.
  * 'groupColIdx' receives an array of column numbers for the GROUP BY
  * expressions (if there are any) in the subplan's target list.
  *
- * The result is the targetlist to be passed to the subplan.  Also,
- * the parent tlist is modified so that any nontrivial targetlist items that
- * exactly match GROUP BY items are replaced by simple Var nodes referencing
- * those outputs of the subplan.  This avoids redundant recalculations in
- * cases like
- *		SELECT a+1, ... GROUP BY a+1
- * Note, however, that other varnodes in the parent's targetlist (and
- * havingQual, if any) will still need to be updated to refer to outputs
- * of the subplan.	This routine is quite large enough already, so we do
- * that later.
+ * The result is the targetlist to be passed to the subplan.
  *---------------
  */
 static List *
@@ -368,14 +388,8 @@ make_subplanTargetList(Query *parse,
 					   AttrNumber **groupColIdx)
 {
 	List	   *sub_tlist;
-	List	   *prnt_tlist;
-	List	   *sl,
-			   *gl;
-	List	   *glc = NIL;
-	List	   *extravars = NIL;
+	List	   *extravars;
 	int			numCols;
-	AttrNumber *grpColIdx = NULL;
-	int			next_resno = 1;
 
 	*groupColIdx = NULL;
 
@@ -387,247 +401,151 @@ make_subplanTargetList(Query *parse,
 		return tlist;
 
 	/*
-	 * If grouping, make a working copy of groupClause list (which we use
-	 * just to verify that we found all the groupClause items in tlist).
-	 * Also allocate space to remember where the group columns are in the
-	 * subplan tlist.
+	 * Otherwise, start with a "flattened" tlist (having just the vars
+	 * mentioned in the targetlist and HAVING qual).
 	 */
-	numCols = length(parse->groupClause);
-	if (numCols > 0)
-	{
-		glc = listCopy(parse->groupClause);
-		grpColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
-		*groupColIdx = grpColIdx;
-	}
-
-	sub_tlist = new_unsorted_tlist(tlist);		/* make a modifiable copy */
+	sub_tlist = flatten_tlist(tlist);
+	extravars = pull_var_clause(parse->havingQual);
+	sub_tlist = add_to_flat_tlist(sub_tlist, extravars);
+	freeList(extravars);
 
 	/*
-	 * Step 1: build grpColIdx by finding targetlist items that match
-	 * GroupBy entries.  If there are aggregates, remove non-GroupBy items
-	 * from sub_tlist, and reset its resnos accordingly.  When we leave an
-	 * expression in the subplan tlist, modify the parent tlist to copy
-	 * the value from the subplan output rather than re-evaluating it.
+	 * If grouping, create sub_tlist entries for all GROUP BY expressions
+	 * (GROUP BY items that are simple Vars should be in the list already),
+	 * and make an array showing where the group columns are in the sub_tlist.
 	 */
-	prnt_tlist = tlist;			/* scans parent tlist in sync with sl */
-	foreach(sl, sub_tlist)
+	numCols = length(parse->groupClause);
+	if (numCols > 0)
 	{
-		TargetEntry *te = (TargetEntry *) lfirst(sl);
-		TargetEntry *parentte = (TargetEntry *) lfirst(prnt_tlist);
-		Resdom	   *resdom = te->resdom;
-		bool		keepInSubPlan = true;
-		bool		foundGroupClause = false;
 		int			keyno = 0;
+		AttrNumber *grpColIdx;
+		List	   *gl;
+
+		grpColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
+		*groupColIdx = grpColIdx;
 
 		foreach(gl, parse->groupClause)
 		{
-			GroupClause *grpcl = (GroupClause *) lfirst(gl);
+			GroupClause	   *grpcl = (GroupClause *) lfirst(gl);
+			Node		   *groupexpr = get_sortgroupclause_expr(grpcl, tlist);
+			TargetEntry	   *te = NULL;
+			List		   *sl;
 
-			keyno++;			/* sort key # for this GroupClause */
-			if (grpcl->tleGroupref == resdom->resgroupref)
+			/* Find or make a matching sub_tlist entry */
+			foreach(sl, sub_tlist)
 			{
-				/* Found a matching groupclause; record info for sorting */
-				foundGroupClause = true;
-				resdom->reskey = keyno;
-				resdom->reskeyop = get_opcode(grpcl->grpOpoid);
-				grpColIdx[keyno - 1] = next_resno;
-
-				/*
-				 * Remove groupclause from our list of unmatched
-				 * groupclauses. NB: this depends on having used a shallow
-				 * listCopy() above.
-				 */
-				glc = lremove((void *) grpcl, glc);
-				break;
+				te = (TargetEntry *) lfirst(sl);
+				if (equal(groupexpr, te->expr))
+					break;
 			}
-		}
-
-		if (!foundGroupClause)
-		{
-
-			/*
-			 * Non-GroupBy entry: remove it from subplan if there are
-			 * aggregates in query - it will be evaluated by Aggregate
-			 * plan. But do not remove simple-Var entries; we'd just have
-			 * to add them back anyway, and we risk confusing
-			 * INSERT/UPDATE.
-			 */
-			if (parse->hasAggs && !IsA(te->expr, Var))
-				keepInSubPlan = false;
-		}
-
-		if (keepInSubPlan)
-		{
-			/* Assign new sequential resnos to subplan tlist items */
-			resdom->resno = next_resno++;
-			if (!IsA(parentte->expr, Var))
+			if (! sl)
 			{
-
-				/*
-				 * Since the item is being computed in the subplan, we can
-				 * just make a Var node to reference it in the outer plan,
-				 * rather than recomputing it there. Note we use varnoold
-				 * = -1 as a flag to let replace_vars_with_subplan_refs
-				 * know it needn't change this Var node. If it's only a
-				 * Var anyway, we leave it alone for now;
-				 * replace_vars_with_subplan_refs will fix it later.
-				 */
-				parentte->expr = (Node *) makeVar(1, resdom->resno,
-												  resdom->restype,
-												  resdom->restypmod,
-												  0, -1, resdom->resno);
+				te = makeTargetEntry(makeResdom(length(sub_tlist) + 1,
+												exprType(groupexpr),
+												exprTypmod(groupexpr),
+												NULL,
+												(Index) 0,
+												(Oid) 0,
+												false),
+									 groupexpr);
+				sub_tlist = lappend(sub_tlist, te);
 			}
-		}
-		else
-		{
-
-			/*
-			 * Remove this tlist item from the subplan, but remember the
-			 * vars it needs.  The outer tlist item probably needs
-			 * changes, but that will happen later.
-			 */
-			sub_tlist = lremove(te, sub_tlist);
-			extravars = nconc(extravars, pull_var_clause(te->expr));
-		}
-
-		prnt_tlist = lnext(prnt_tlist);
-	}
-
-	/* We should have found all the GROUP BY clauses in the tlist. */
-	if (length(glc) != 0)
-		elog(ERROR, "make_subplanTargetList: GROUP BY attribute not found in target list");
 
-	/*
-	 * Add subplan targets for any variables needed by removed tlist
-	 * entries that aren't otherwise mentioned in the subplan target list.
-	 * We'll also need targets for any variables seen only in HAVING.
-	 */
-	extravars = nconc(extravars, pull_var_clause(parse->havingQual));
-
-	foreach(gl, extravars)
-	{
-		Var		   *v = (Var *) lfirst(gl);
-
-		if (tlist_member(v, sub_tlist) == NULL)
-		{
-
-			/*
-			 * Make sure sub_tlist element is a fresh object not shared
-			 * with any other structure; not sure if anything will break
-			 * if it is shared, but better to be safe...
-			 */
-			sub_tlist = lappend(sub_tlist,
-								create_tl_element((Var *) copyObject(v),
-												  next_resno));
-			next_resno++;
+			/* and save its resno */
+			grpColIdx[keyno++] = te->resdom->resno;
 		}
 	}
 
 	return sub_tlist;
 }
 
+/*
+ * make_groupplan
+ *		Add a Group node for GROUP BY processing.
+ *		If we couldn't make the subplan produce presorted output for grouping,
+ *		first add an explicit Sort node.
+ */
 static Plan *
 make_groupplan(List *group_tlist,
 			   bool tuplePerGroup,
 			   List *groupClause,
 			   AttrNumber *grpColIdx,
+			   bool is_sorted,
 			   Plan *subplan)
 {
-	List	   *sort_tlist;
-	List	   *sl;
-	Sort	   *sortplan;
-	Group	   *grpplan;
 	int			numCols = length(groupClause);
+	Group	   *grpplan;
 
-	/*
-	 * Make the targetlist for the Sort node; it always just references
-	 * each of the corresponding target items of the subplan.  We need to
-	 * ensure that simple Vars in the subplan's target list are
-	 * recognizable by replace_vars_with_subplan_refs when it's applied to
-	 * the Sort/Group target list, so copy up their varnoold/varoattno.
-	 */
-	sort_tlist = NIL;
-	foreach(sl, subplan->targetlist)
+	if (! is_sorted)
 	{
-		TargetEntry *te = (TargetEntry *) lfirst(sl);
-		Resdom	   *resdom = te->resdom;
-		Var		   *newvar;
+		/*
+		 * 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.)
+		 */
+		List	   *sort_tlist = new_unsorted_tlist(subplan->targetlist);
+		int			keyno = 0;
+		List	   *gl;
 
-		if (IsA(te->expr, Var))
+		foreach(gl, groupClause)
 		{
-			Var		   *subvar = (Var *) te->expr;
+			GroupClause	   *grpcl = (GroupClause *) lfirst(gl);
+			TargetEntry	   *te = nth(grpColIdx[keyno]-1, sort_tlist);
+			Resdom		   *resdom = te->resdom;
 
-			newvar = makeVar(1, resdom->resno,
-							 resdom->restype, resdom->restypmod,
-							 0, subvar->varnoold, subvar->varoattno);
-		}
-		else
-		{
-			newvar = makeVar(1, resdom->resno,
-							 resdom->restype, resdom->restypmod,
-							 0, -1, resdom->resno);
+			/*
+			 * 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 = get_opcode(grpcl->sortop);
+			}
 		}
 
-		sort_tlist = lappend(sort_tlist,
-						   makeTargetEntry((Resdom *) copyObject(resdom),
-										   (Node *) newvar));
+		subplan = (Plan *) make_sort(sort_tlist,
+									 _NONAME_RELATION_ID_,
+									 subplan,
+									 keyno);
 	}
 
 	/*
-	 * Make the Sort node
+	 * Fix variables in tlist (should be done somewhere else?)
 	 */
-	sortplan = make_sort(sort_tlist,
-						 _NONAME_RELATION_ID_,
-						 subplan,
-						 numCols);
-	sortplan->plan.cost = subplan->cost;		/* XXX assume no cost */
-
-	/*
-	 * If the caller gave us a target list, use it after fixing the
-	 * variables. If not, we need the same sort of "repeater" tlist as for
-	 * the Sort node.
-	 */
-	if (group_tlist)
-	{
-		group_tlist = copyObject(group_tlist);	/* necessary?? */
-		replace_tlist_with_subplan_refs(group_tlist,
-										(Index) 0,
-										subplan->targetlist);
-	}
-	else
-		group_tlist = copyObject(sort_tlist);
+	group_tlist = copyObject(group_tlist);	/* necessary?? */
+	replace_tlist_with_subplan_refs(group_tlist,
+									(Index) 0,
+									subplan->targetlist);
 
 	/*
 	 * Make the Group node
 	 */
 	grpplan = make_group(group_tlist, tuplePerGroup, numCols,
-						 grpColIdx, sortplan);
+						 grpColIdx, subplan);
 
 	return (Plan *) grpplan;
 }
 
 /*
  * make_sortplan
- *	  Returns a sortplan which is basically a SORT node attached to the
- *	  top of the plan returned from the planner.  It also adds the
- *	   cost of sorting into the plan.
- *
- * sortkeys: ( resdom1 resdom2 resdom3 ...)
- * sortops:  (sortop1 sortop2 sortop3 ...)
+ *	  Add a Sort node to implement an explicit ORDER BY clause.
  */
 static Plan *
 make_sortplan(List *tlist, List *sortcls, Plan *plannode)
 {
-	Plan	   *sortplan = (Plan *) NULL;
-	List	   *temp_tlist = NIL;
-	List	   *i = NIL;
-	Resdom	   *resnode = (Resdom *) NULL;
-	Resdom	   *resdom = (Resdom *) NULL;
-	int			keyno = 1;
+	List	   *temp_tlist;
+	List	   *i;
+	int			keyno = 0;
 
 	/*
-	 * First make a copy of the tlist so that we don't corrupt the the
-	 * original .
+	 * First make a copy of the tlist so that we don't corrupt the
+	 * original.
 	 */
 
 	temp_tlist = new_unsorted_tlist(tlist);
@@ -635,35 +553,38 @@ make_sortplan(List *tlist, List *sortcls, Plan *plannode)
 	foreach(i, sortcls)
 	{
 		SortClause *sortcl = (SortClause *) lfirst(i);
+		Index		refnumber = sortcl->tleSortGroupRef;
+		TargetEntry *tle = NULL;
+		Resdom	   *resdom;
+		List	   *l;
 
-		resnode = sortcl->resdom;
-		resdom = tlist_resdom(temp_tlist, resnode);
+		foreach(l, temp_tlist)
+		{
+			tle = (TargetEntry *) lfirst(l);
+			if (tle->resdom->ressortgroupref == refnumber)
+				break;
+		}
+		if (l == NIL)
+			elog(ERROR, "make_sortplan: ORDER BY expression not found in targetlist");
+		resdom = tle->resdom;
 
 		/*
-		 * Order the resdom keys and replace the operator OID for each key
-		 * with the regproc OID.
+		 * 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!
 		 */
-		resdom->reskey = keyno;
-		resdom->reskeyop = get_opcode(sortcl->opoid);
-		keyno += 1;
+		if (resdom->reskey == 0)
+		{
+			/* OK, insert the ordering info needed by the executor. */
+			resdom->reskey = ++keyno;
+			resdom->reskeyop = get_opcode(sortcl->sortop);
+		}
 	}
 
-	sortplan = (Plan *) make_sort(temp_tlist,
-								  _NONAME_RELATION_ID_,
-								  (Plan *) plannode,
-								  length(sortcls));
-
-	/*
-	 * XXX Assuming that an internal sort has no. cost. This is wrong, but
-	 * given that at this point, we don't know the no. of tuples returned,
-	 * etc, we can't do better than to add a constant cost. This will be
-	 * fixed once we move the sort further into the planner, but for now
-	 * ... functionality....
-	 */
-
-	sortplan->cost = plannode->cost;
-
-	return sortplan;
+	return (Plan *) make_sort(temp_tlist,
+							  _NONAME_RELATION_ID_,
+							  plannode,
+							  keyno);
 }
 
 /*
@@ -828,187 +749,3 @@ pg_checkretval(Oid rettype, List *queryTreeList)
 	/* success */
 	return;
 }
-
-
-/* ----------
- * Support function for get scan direction to omit sortplan
- * ----------
- */
-static TargetEntry *
-get_matching_tle(Plan *plan, Resdom *resdom)
-{
-	List	   *i;
-	TargetEntry *tle;
-
-	foreach(i, plan->targetlist)
-	{
-		tle = (TargetEntry *) lfirst(i);
-		if (tle->resdom->resno == resdom->resno)
-			return tle;
-	}
-	return NULL;
-}
-
-
-/* ----------
- * Check if a user requested ORDER BY is already satisfied by
- * the choosen index scan.
- *
- * Returns the direction of Index scan to omit sort,
- * if sort is required returns NoMovementScanDirection  
- *
- * ----------
- */
-static ScanDirection
-get_dir_to_omit_sortplan(List *sortcls, Plan *plan)
-{
-	Relation	indexRel;
-	IndexScan  *indexScan;
-	Oid			indexId;
-	List	   *i;
-	HeapTuple	htup;
-	Form_pg_index index_tup;
-	int			key_no = 0;
-	ScanDirection   dir, nodir = NoMovementScanDirection;
-
-	dir = nodir;
-	/* ----------
-	 * Must be an IndexScan
-	 * ----------
-	 */
-	if (nodeTag(plan) != T_IndexScan)
-		return nodir;
-
-	indexScan = (IndexScan *) plan;
-
-	/* ----------
-	 * Should not have left- or righttree
-	 * ----------
-	 */
-	if (plan->lefttree != NULL)
-		return nodir;
-	if (plan->righttree != NULL)
-		return nodir;
-
-	/* ----------
-	 * Must be a single index scan
-	 * ----------
-	 */
-	if (length(indexScan->indxid) != 1)
-		return nodir;
-
-	/* ----------
-	 * Indices can only have up to 8 attributes. So an ORDER BY using
-	 * more that 8 attributes could never be satisfied by an index.
-	 * ----------
-	 */
-	if (length(sortcls) > 8)
-		return nodir;
-
-	/* ----------
-	 * The choosen Index must be a btree
-	 * ----------
-	 */
-	indexId = lfirsti(indexScan->indxid);
-
-	indexRel = index_open(indexId);
-	if (strcmp(nameout(&(indexRel->rd_am->amname)), "btree") != 0)
-	{
-		heap_close(indexRel);
-		return nodir;
-	}
-	heap_close(indexRel);
-
-	/* ----------
-	 * Fetch the index tuple
-	 * ----------
-	 */
-	htup = SearchSysCacheTuple(INDEXRELID,
-							   ObjectIdGetDatum(indexId), 0, 0, 0);
-	if (!HeapTupleIsValid(htup))
-		elog(ERROR, "cache lookup for index %u failed", indexId);
-	index_tup = (Form_pg_index) GETSTRUCT(htup);
-
-	/* ----------
-	 * Check if all the sort clauses match the attributes in the index
-	 * ----------
-	 */
-	foreach(i, sortcls)
-	{
-		SortClause *sortcl;
-		Resdom	   *resdom;
-		TargetEntry *tle;
-		Var		   *var;
-
-		sortcl = (SortClause *) lfirst(i);
-
-		resdom = sortcl->resdom;
-		tle = get_matching_tle(plan, resdom);
-		if (tle == NULL)
-		{
-			/* ----------
-			 * Could this happen?
-			 * ----------
-			 */
-			return nodir;
-		}
-		if (nodeTag(tle->expr) != T_Var)
-		{
-			/* ----------
-			 * The target list expression isn't a var, so it
-			 * cannot be the indexed attribute
-			 * ----------
-			 */
-			return nodir;
-		}
-		var = (Var *) (tle->expr);
-
-		if (var->varno != indexScan->scan.scanrelid)
-		{
-			/* ----------
-			 * This Var isn't from the scan relation. So it isn't
-			 * that of the index
-			 * ----------
-			 */
-			return nodir;
-		}
-
-		if (var->varattno != index_tup->indkey[key_no])
-		{
-			/* ----------
-			 * It isn't the indexed attribute.
-			 * ----------
-			 */
-			return nodir;
-		}
-
-		if (oprid(oper("<", resdom->restype, resdom->restype, FALSE)) != sortcl->opoid)
-		{
-			/* ----------
-			 * Sort order isn't in ascending order.
-			 * ----------
-			 */
-			if (ScanDirectionIsForward(dir))
-				return nodir;
-			dir = BackwardScanDirection;
-		}
-		else
-		{
-			/* ----------
-			 * Sort order is in ascending order.
-			 * ----------
-			*/
-			if (ScanDirectionIsBackward(dir))
-				return nodir;
-			dir = ForwardScanDirection;
-		}
-
-		key_no++;
-	}
-
-	/* ----------
-	 * Index matches ORDER BY - sort not required
-	 * ----------
-	 */
-	return dir;
-}
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 6471ad4e41bca77bd00da20169e030baa5e03047..1492df8b03015b4ca8615a4a76ec2aa5ce7a4bcf 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.55 1999/08/18 04:15:16 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.56 1999/08/21 03:49:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -52,7 +52,6 @@ static void replace_vars_with_subplan_refs(Node *clause,
 										   List *subplanTargetList);
 static bool replace_vars_with_subplan_refs_walker(Node *node,
 					replace_vars_with_subplan_refs_context *context);
-static List *pull_agg_clause(Node *clause);
 static bool pull_agg_clause_walker(Node *node, List **listptr);
 static bool check_having_for_ungrouped_vars_walker(Node *node,
 					check_having_for_ungrouped_vars_context *context);
@@ -416,23 +415,9 @@ replace_vars_with_subplan_refs_walker(Node *node,
 		return false;
 	if (IsA(node, Var))
 	{
-		/*
-		 * It could be that this varnode has been created by make_groupplan
-		 * and is already set up to reference the subplan target list. We
-		 * recognize that case by varno = 1, varnoold = -1, varattno =
-		 * varoattno, and varlevelsup = 0.	(Probably ought to have an
-		 * explicit flag, but this should do for now.)
-		 */
 		Var		   *var = (Var *) node;
 		TargetEntry *subplanVar;
 
-		if (var->varno == (Index) 1 &&
-			var->varnoold == ((Index) -1) &&
-			var->varattno == var->varoattno &&
-			var->varlevelsup == 0)
-			return false;		/* OK to leave it alone */
-
-		/* Otherwise it had better be in the subplan list. */
 		subplanVar = match_varid(var, context->subplanTargetList);
 		if (!subplanVar)
 			elog(ERROR, "replace_vars_with_subplan_refs: variable not in target list");
@@ -461,7 +446,6 @@ replace_vars_with_subplan_refs_walker(Node *node,
  *	  the tuples returned by its left tree subplan.
  *	* If there is a qual list (from a HAVING clause), similarly update
  *	  vars in it to point to the subplan target list.
- *	* Generate the aggNode->aggs list of Aggref nodes contained in the Agg.
  *
  * The return value is TRUE if all qual clauses include Aggrefs, or FALSE
  * if any do not (caller may choose to raise an error condition).
@@ -475,7 +459,6 @@ set_agg_tlist_references(Agg *aggNode)
 	bool		all_quals_ok;
 
 	subplanTargetList = aggNode->plan.lefttree->targetlist;
-	aggNode->aggs = NIL;
 
 	foreach(tl, aggNode->plan.targetlist)
 	{
@@ -484,24 +467,19 @@ set_agg_tlist_references(Agg *aggNode)
 		replace_vars_with_subplan_refs(tle->expr,
 									   (Index) 0,
 									   subplanTargetList);
-		aggNode->aggs = nconc(pull_agg_clause(tle->expr), aggNode->aggs);
 	}
 
 	all_quals_ok = true;
 	foreach(ql, aggNode->plan.qual)
 	{
 		Node	   *qual = lfirst(ql);
-		List	   *qualaggs;
 
 		replace_vars_with_subplan_refs(qual,
 									   (Index) 0,
 									   subplanTargetList);
-		qualaggs = pull_agg_clause(qual);
-		if (qualaggs == NIL)
+		if (pull_agg_clause(qual) == NIL)
 			all_quals_ok = false;		/* this qual clause has no agg
 										 * functions! */
-		else
-			aggNode->aggs = nconc(qualaggs, aggNode->aggs);
 	}
 
 	return all_quals_ok;
@@ -514,7 +492,7 @@ set_agg_tlist_references(Agg *aggNode)
  *	  Returns list of Aggref nodes found.  Note the nodes themselves are not
  *	  copied, only referenced.
  */
-static List *
+List *
 pull_agg_clause(Node *clause)
 {
 	List	   *result = NIL;
@@ -545,10 +523,6 @@ pull_agg_clause_walker(Node *node, List **listptr)
  * exprIsAggOrGroupCol()).	But that routine currently does not check subplans,
  * because the necessary info is not computed until the planner runs.
  * This ought to be cleaned up someday.
- *
- * NOTE: the havingClause has been cnf-ified, so AND subclauses have been
- * turned into a plain List.  Thus, this routine has to cope with List nodes
- * where the routine above does not...
  */
 
 void
@@ -588,10 +562,13 @@ check_having_for_ungrouped_vars_walker(Node *node,
 
 			foreach(gl, context->groupClause)
 			{
-				Var	   *groupexpr = get_groupclause_expr(lfirst(gl),
-														 context->targetList);
+				GroupClause	   *gcl = lfirst(gl);
+				Node		   *groupexpr;
 
-				if (var_equal((Var *) thisarg, groupexpr))
+				groupexpr = get_sortgroupclause_expr(gcl,
+													 context->targetList);
+				/* XXX is var_equal correct, or should we use equal()? */
+				if (var_equal((Var *) thisarg, (Var *) groupexpr))
 				{
 					contained_in_group_clause = true;
 					break;
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index d0022b4cbc04eff794d47690e1c17627a2cbb16c..188379c9a2dbf0fac71b8aa04a9aa9cf49c8bf25 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -6,7 +6,7 @@
  * Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.21 1999/07/16 04:59:20 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.22 1999/08/21 03:49:03 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -552,9 +552,7 @@ SS_finalize_plan(Plan *plan)
 			break;
 
 		case T_Agg:
-			finalize_primnode_walker((Node *) ((Agg *) plan)->aggs,
-									 &results);
-			Assert(results.subplans == NIL);
+			/* XXX Code used to reject subplans in Aggref args; needed?? */
 			break;
 
 		case T_SeqScan:
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index 0cc4780807e85d0b8ac6f5d33e08f1d593058992..2a9ddfc716a2dc2ef4cb906544d01fd883a4eb76 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.29 1999/08/09 03:13:31 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.30 1999/08/21 03:49:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -163,7 +163,6 @@ replace_matching_resname(List *new_tlist, List *old_tlist)
 		{
 			TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
 
-			old_tle = lfirst(temp);
 			if (!strcmp(old_tle->resdom->resname,
 						new_tle->resdom->resname))
 			{
@@ -174,6 +173,7 @@ replace_matching_resname(List *new_tlist, List *old_tlist)
 
 		if (matching_old_tl)
 		{
+			/* XXX safe to modify old resdom? */
 			matching_old_tl->resdom->resno = new_tle->resdom->resno;
 			t_list = lappend(t_list, matching_old_tl);
 		}
@@ -197,45 +197,42 @@ replace_matching_resname(List *new_tlist, List *old_tlist)
 	{
 		TargetEntry *old_tle,
 				   *new_tl;
-		Resdom	   *newresno;
 
 		old_tle = lfirst(temp);
 		if (old_tle->resdom->resno < 0)
 		{
-			newresno = (Resdom *) copyObject((Node *) old_tle->resdom);
-			newresno->resno = length(t_list) + 1;
-			newresno->resjunk = true;
-			new_tl = makeTargetEntry(newresno, old_tle->expr);
+			Resdom	   *newresdom;
+
+			newresdom = (Resdom *) copyObject((Node *) old_tle->resdom);
+			newresdom->resno = length(t_list) + 1;
+			newresdom->resjunk = true;
+			new_tl = makeTargetEntry(newresdom, old_tle->expr);
 			t_list = lappend(t_list, new_tl);
 		}
-
 		/*
 		 * Also it is possible that the parser or rewriter added some junk
-		 * attributes to hold GROUP BY expressions which are not part of
+		 * attributes to hold ORDER/GROUP BY expressions which are not part of
 		 * the result attributes. We can simply identify them by looking
-		 * at the resgroupref in the TLE's resdom, which is a unique
-		 * number telling which TLE belongs to which GroupClause.
+		 * at the ressortgroupref in the TLE's resdom, which is a unique
+		 * number telling which TLE belongs to which Sort/GroupClause.
 		 */
-		if (old_tle->resdom->resgroupref > 0)
+		else if (old_tle->resdom->ressortgroupref > 0)
 		{
-			bool		already_there = FALSE;
-			TargetEntry *new_tle;
-			Resdom	   *newresno;
+			bool		already_there = false;
 
 			/*
 			 * Check if the tle is already in the new list
 			 */
 			foreach(i, t_list)
 			{
-				new_tle = (TargetEntry *) lfirst(i);
+				TargetEntry *new_tle = (TargetEntry *) lfirst(i);
 
-				if (new_tle->resdom->resgroupref ==
-					old_tle->resdom->resgroupref)
+				if (new_tle->resdom->ressortgroupref ==
+					old_tle->resdom->ressortgroupref)
 				{
-					already_there = TRUE;
+					already_there = true;
 					break;
 				}
-
 			}
 
 			/*
@@ -243,10 +240,12 @@ replace_matching_resname(List *new_tlist, List *old_tlist)
 			 */
 			if (!already_there)
 			{
-				newresno = (Resdom *) copyObject((Node *) old_tle->resdom);
-				newresno->resno = length(t_list) + 1;
-				newresno->resjunk = true;
-				new_tl = makeTargetEntry(newresno, old_tle->expr);
+				Resdom	   *newresdom;
+
+				newresdom = (Resdom *) copyObject((Node *) old_tle->resdom);
+				newresdom->resno = length(t_list) + 1;
+				newresdom->resjunk = true;
+				new_tl = makeTargetEntry(newresdom, old_tle->expr);
 				t_list = lappend(t_list, new_tl);
 			}
 		}
diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c
index 21afad92b820b5eaf83d4bf4dd87f4881d5c8597..54e84739d240f364e9323dae36fa1f82fa5ab36f 100644
--- a/src/backend/optimizer/prep/prepunion.c
+++ b/src/backend/optimizer/prep/prepunion.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.38 1999/08/16 02:17:55 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.39 1999/08/21 03:49:05 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -159,7 +159,7 @@ plan_union_queries(Query *parse)
 		union_plans = lcons(union_planner(parse), NIL);
 		union_rts = lcons(parse->rtable, NIL);
 
-		/* Append the remainging UNION ALLs */
+		/* Append the remaining UNION ALLs */
 		foreach(ulist, union_all_queries)
 		{
 			Query	   *union_all_query = lfirst(ulist);
@@ -172,14 +172,19 @@ plan_union_queries(Query *parse)
 	/* We have already split UNION and UNION ALL and we made it consistent */
 	if (!last_union_all_flag)
 	{
+		/* Need SELECT DISTINCT behavior to implement UNION.
+		 * Set uniqueFlag properly, put back the held sortClause,
+		 * and add any missing columns to the sort clause.
+		 */
 		parse->uniqueFlag = "*";
-		parse->sortClause = transformSortClause(NULL, NIL,
-												hold_sortClause,
-												parse->targetList, "*");
+		parse->sortClause = addAllTargetsToSortList(hold_sortClause,
+													parse->targetList);
 	}
 	else
+	{
 		/* needed so we don't take the flag from the first query */
 		parse->uniqueFlag = NULL;
+	}
 
 	/* Make sure we don't try to apply the first query's grouping stuff
 	 * to the Append node, either.  Basically we don't want union_planner
diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c
index 921bb1d671a2627fa9760550b373dd166ca1eb2f..37a790cc3dd5cabd1f27c32ad5d652c0f7862696 100644
--- a/src/backend/optimizer/util/tlist.c
+++ b/src/backend/optimizer/util/tlist.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.38 1999/08/10 03:00:15 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.39 1999/08/21 03:49:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,7 +19,7 @@
 #include "optimizer/tlist.h"
 #include "optimizer/var.h"
 
-static Node *flatten_tlist_vars_mutator(Node *node, List *flat_tlist);
+static Node *unflatten_tlist_mutator(Node *node, List *flat_tlist);
 
 /*****************************************************************************
  *	---------- RELATION node target list routines ----------
@@ -89,7 +89,7 @@ tlist_member(Var *var, List *tlist)
 void
 add_var_to_tlist(RelOptInfo *rel, Var *var)
 {
-	if (tlistentry_member(var, rel->targetlist) == NULL)
+	if (! tlistentry_member(var, rel->targetlist))
 	{
 		/* XXX is copyObject necessary here? */
 		rel->targetlist = lappend(rel->targetlist,
@@ -156,27 +156,6 @@ get_actual_tlist(List *tlist)
  *		---------- GENERAL target list routines ----------
  *****************************************************************************/
 
-/*
- *	 Routine to get the resdom out of a targetlist.
- */
-Resdom *
-tlist_resdom(List *tlist, Resdom *resnode)
-{
-	List	   *i;
-
-	foreach(i, tlist)
-	{
-		TargetEntry *tle = (TargetEntry *) lfirst(i);
-		Resdom	   *resdom = tle->resdom;
-
-		/* Since resnos are supposed to be unique */
-		if (resnode->resno == resdom->resno)
-			return resdom;
-	}
-	return (Resdom *) NULL;
-}
-
-
 /*
  * match_varid
  *	  Searches a target list for an entry matching a given var.
@@ -276,49 +255,68 @@ copy_vars(List *target, List *source)
  * flatten_tlist
  *	  Create a target list that only contains unique variables.
  *
- *
  * 'tlist' is the current target list
  *
  * Returns the "flattened" new target list.
  *
+ * The result is entirely new structure sharing no nodes with the original.
+ * Copying the Var nodes is probably overkill, but be safe for now.
  */
 List *
 flatten_tlist(List *tlist)
 {
 	List	   *vlist = pull_var_clause((Node *) tlist);
-	int			last_resdomno = 1;
-	List	   *new_tlist = NIL;
+	List	   *new_tlist;
+
+	new_tlist = add_to_flat_tlist(NIL, vlist);
+	freeList(vlist);
+	return new_tlist;
+}
+
+/*
+ * add_to_flat_tlist
+ *		Add more vars to a flattened tlist (if they're not already in it)
+ *
+ * 'tlist' is the flattened tlist
+ * 'vars' is a list of var nodes
+ *
+ * Returns the extended tlist.
+ */
+List *
+add_to_flat_tlist(List *tlist, List *vars)
+{
+	int			next_resdomno = length(tlist) + 1;
 	List	   *v;
 
-	foreach(v, vlist)
+	foreach(v, vars)
 	{
 		Var		   *var = lfirst(v);
 
-		if (! tlistentry_member(var, new_tlist))
+		if (! tlistentry_member(var, tlist))
 		{
 			Resdom	   *r;
 
-			r = makeResdom(last_resdomno++,
+			r = makeResdom(next_resdomno++,
 						   var->vartype,
 						   var->vartypmod,
 						   NULL,
 						   (Index) 0,
 						   (Oid) 0,
 						   false);
-			new_tlist = lappend(new_tlist,
-								makeTargetEntry(r, (Node *) var));
+			tlist = lappend(tlist,
+							makeTargetEntry(r, copyObject(var)));
 		}
 	}
-	freeList(vlist);
-
-	return new_tlist;
+	return tlist;
 }
 
 /*
- * flatten_tlist_vars
- *	  Redoes the target list of a query by replacing vars within
+ * unflatten_tlist
+ *	  Reconstructs the target list of a query by replacing vars within
  *	  target expressions with vars from the 'flattened' target list.
  *
+ * XXX is this really necessary?  Why can't we just use the tlist as is?
+ *
  * 'full_tlist' is the original target list
  * 'flat_tlist' is the flattened (var-only) target list
  *
@@ -326,21 +324,21 @@ flatten_tlist(List *tlist)
  *
  */
 List *
-flatten_tlist_vars(List *full_tlist, List *flat_tlist)
+unflatten_tlist(List *full_tlist, List *flat_tlist)
 {
-	return (List *) flatten_tlist_vars_mutator((Node *) full_tlist,
-											   flat_tlist);
+	return (List *) unflatten_tlist_mutator((Node *) full_tlist,
+											flat_tlist);
 }
 
 static Node *
-flatten_tlist_vars_mutator(Node *node, List *flat_tlist)
+unflatten_tlist_mutator(Node *node, List *flat_tlist)
 {
 	if (node == NULL)
 		return NULL;
 	if (IsA(node, Var))
 		return (Node *) get_expr(match_varid((Var *) node,
 											 flat_tlist));
-	return expression_tree_mutator(node, flatten_tlist_vars_mutator,
+	return expression_tree_mutator(node, unflatten_tlist_mutator,
 								   (void *) flat_tlist);
 }
 
@@ -354,20 +352,28 @@ get_expr(TargetEntry *tle)
 	return (Var *) tle->expr;
 }
 
-
-Var *
-get_groupclause_expr(GroupClause *groupClause, List *targetList)
+/*
+ * get_sortgroupclause_expr
+ *		Find the targetlist entry matching the given SortClause
+ *		(or GroupClause) by ressortgroupref, and return its expression.
+ *
+ * Because GroupClause is typedef'd as SortClause, either kind of
+ * node can be passed without casting.
+ */
+Node *
+get_sortgroupclause_expr(SortClause *sortClause, List *targetList)
 {
+	Index		refnumber = sortClause->tleSortGroupRef;
 	List	   *l;
 
 	foreach(l, targetList)
 	{
 		TargetEntry *tle = (TargetEntry *) lfirst(l);
-		if (tle->resdom->resgroupref == groupClause->tleGroupref)
-			return get_expr(tle);
+
+		if (tle->resdom->ressortgroupref == refnumber)
+			return tle->expr;
 	}
 
-	elog(ERROR,
-	"get_groupclause_expr: GROUP BY expression not found in targetlist");
-	return NULL;
+	elog(ERROR, "get_sortgroupclause_expr: ORDER/GROUP BY expression not found in targetlist");
+	return NULL;				/* keep compiler quiet */
 }
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index e3c9feb677c67389ff8a9bac2d336629e46fdb2a..2c4d1c7a7e824fc8535bee0a86f1db8825652a09 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -5,7 +5,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- *	$Id: analyze.c,v 1.117 1999/08/15 06:46:49 thomas Exp $
+ *	$Id: analyze.c,v 1.118 1999/08/21 03:48:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -292,7 +292,6 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
 	 * transformSortClause because it also handles uniqueFlag.
 	 */
 	qry->sortClause = transformSortClause(pstate,
-										  NIL,
 										  NIL,
 										  qry->targetList,
 										  qry->uniqueFlag);
@@ -1039,7 +1038,6 @@ transformSelectStmt(ParseState *pstate, SelectStmt *stmt)
 
 	qry->sortClause = transformSortClause(pstate,
 										  stmt->sortClause,
-										  NIL,
 										  qry->targetList,
 										  qry->uniqueFlag);
 
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 3cca6d681fc28427973c68dc0d0c5083e4fee7e3..a5007822976e19e937a8a342158c7b7c074ad161 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.27 1999/08/16 02:10:13 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.28 1999/08/21 03:48:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -154,7 +154,7 @@ parseCheckAggregates(ParseState *pstate, Query *qry)
 		GroupClause *grpcl = lfirst(tl);
 		Node		*expr;
 
-		expr = (Node *) get_groupclause_expr(grpcl, qry->targetList);
+		expr = get_sortgroupclause_expr(grpcl, qry->targetList);
 		if (contain_agg_clause(expr))
 			elog(ERROR, "Aggregates not allowed in GROUP BY clause");
 		groupClauses = lcons(expr, groupClauses);
@@ -293,10 +293,8 @@ ParseAgg(ParseState *pstate, char *aggname, Oid basetype,
 	aggref->aggname = pstrdup(aggname);
 	aggref->basetype = aggform->aggbasetype;
 	aggref->aggtype = fintype;
-
 	aggref->target = lfirst(target);
-	if (usenulls)
-		aggref->usenulls = true;
+	aggref->usenulls = usenulls;
 
 	pstate->p_hasAggs = true;
 
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 275d131baba9a45cb9026f2baa96fba7139c0280..234987fb5f0472ba7ea3e545575b251169dc2e5d 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.43 1999/08/16 02:10:13 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.44 1999/08/21 03:48:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,6 +34,9 @@ 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);
+static List *addTargetToSortList(TargetEntry *tle, List *sortlist,
+								 List *targetlist, char *opname);
+static bool exprIsInSortList(Node *expr, List *sortList, List *targetList);
 
 #ifdef ENABLE_OUTER_JOINS
 static Node *transformUsingClause(ParseState *pstate, List *onList,
@@ -464,39 +467,25 @@ List *
 transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
 {
 	List	   *glist = NIL,
-			   *gl,
-			   *othergl;
-	int			nextgroupref = 1;
+			   *gl;
 
 	foreach(gl, grouplist)
 	{
-		TargetEntry *restarget;
-		Resdom	   *resdom;
+		TargetEntry *tle;
 
-		restarget = findTargetlistEntry(pstate, lfirst(gl),
-										targetlist, GROUP_CLAUSE);
-		resdom = restarget->resdom;
+		tle = findTargetlistEntry(pstate, lfirst(gl),
+								  targetlist, GROUP_CLAUSE);
 
 		/* avoid making duplicate grouplist entries */
-		foreach(othergl, glist)
-		{
-			GroupClause *gcl = (GroupClause *) lfirst(othergl);
-
-			if (equal(get_groupclause_expr(gcl, targetlist),
-					  restarget->expr))
-				break;
-		}
-
-		if (othergl == NIL)		/* not in grouplist already */
+		if (! exprIsInSortList(tle->expr, glist, targetlist))
 		{
 			GroupClause *grpcl = makeNode(GroupClause);
 
-			grpcl->tleGroupref = nextgroupref++;
-			resdom->resgroupref = grpcl->tleGroupref;
+			grpcl->tleSortGroupRef = assignSortGroupRef(tle, targetlist);
 
-			grpcl->grpOpoid = oprid(oper("<",
-										 resdom->restype,
-										 resdom->restype, false));
+			grpcl->sortop = oprid(oper("<",
+									   tle->resdom->restype,
+									   tle->resdom->restype, false));
 
 			glist = lappend(glist, grpcl);
 		}
@@ -513,141 +502,169 @@ transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist)
 List *
 transformSortClause(ParseState *pstate,
 					List *orderlist,
-					List *sortlist,
 					List *targetlist,
 					char *uniqueFlag)
 {
-	List	   *s = NIL;
-
-	while (orderlist != NIL)
-	{
-		SortGroupBy *sortby = lfirst(orderlist);
-		SortClause *sortcl = makeNode(SortClause);
-		TargetEntry *restarget;
-		Resdom	   *resdom;
-
-		restarget = findTargetlistEntry(pstate, sortby->node,
-										targetlist, ORDER_CLAUSE);
+	List	   *sortlist = NIL;
+	List	   *olitem;
 
-		sortcl->resdom = resdom = restarget->resdom;
+	/* Transform all the explicit ORDER BY clauses */
 
-		/*
-		 * if we have InvalidOid, then this is a NULL field and don't need
-		 * to sort
-		 */
-		if (resdom->restype == InvalidOid)
-			resdom->restype = INT4OID;
-
-		sortcl->opoid = oprid(oper(sortby->useOp,
-								   resdom->restype,
-								   resdom->restype, false));
-		if (sortlist == NIL)
-			s = sortlist = lcons(sortcl, NIL);
-		else
-		{
-			List	   *i;
+	foreach(olitem, orderlist)
+	{
+		SortGroupBy	   *sortby = lfirst(olitem);
+		TargetEntry	   *tle;
 
-			foreach(i, sortlist)
-			{
-				SortClause *scl = (SortClause *) lfirst(i);
+		tle = findTargetlistEntry(pstate, sortby->node,
+								  targetlist, ORDER_CLAUSE);
 
-				if (scl->resdom == sortcl->resdom)
-					break;
-			}
-			if (i == NIL)		/* not in sortlist already */
-			{
-				lnext(s) = lcons(sortcl, NIL);
-				s = lnext(s);
-			}
-			else
-				pfree(sortcl);	/* get rid of this */
-		}
-		orderlist = lnext(orderlist);
+		sortlist = addTargetToSortList(tle, sortlist, targetlist,
+									   sortby->useOp);
 	}
 
+	/* If we have a DISTINCT clause, add any necessary entries to
+	 * the sortlist to ensure that all the DISTINCT columns will be
+	 * sorted.  A subsequent UNIQUE pass will then do the right thing.
+	 */
+
 	if (uniqueFlag)
 	{
-		List	   *i;
-
 		if (uniqueFlag[0] == '*')
 		{
-
 			/*
 			 * concatenate all elements from target list that are not
 			 * already in the sortby list
 			 */
-			foreach(i, targetlist)
-			{
-				TargetEntry *tlelt = (TargetEntry *) lfirst(i);
-
-				s = sortlist;
-				while (s != NIL)
-				{
-					SortClause *sortcl = lfirst(s);
-
-					/*
-					 * We use equal() here because we are called for UNION
-					 * from the optimizer, and at that point, the sort
-					 * clause resdom pointers don't match the target list
-					 * resdom pointers
-					 */
-					if (equal(sortcl->resdom, tlelt->resdom))
-						break;
-					s = lnext(s);
-				}
-				if (s == NIL)
-				{
-					/* not a member of the sortclauses yet */
-					SortClause *sortcl = makeNode(SortClause);
-
-					if (tlelt->resdom->restype == InvalidOid)
-						tlelt->resdom->restype = INT4OID;
-
-					sortcl->resdom = tlelt->resdom;
-					sortcl->opoid = any_ordering_op(tlelt->resdom->restype);
-
-					sortlist = lappend(sortlist, sortcl);
-				}
-			}
+			sortlist = addAllTargetsToSortList(sortlist, targetlist);
 		}
 		else
 		{
-			TargetEntry *tlelt = NULL;
+			TargetEntry *tle = NULL;
 			char	   *uniqueAttrName = uniqueFlag;
+			List	   *i;
 
 			/* only create sort clause with the specified unique attribute */
 			foreach(i, targetlist)
 			{
-				tlelt = (TargetEntry *) lfirst(i);
-				if (strcmp(tlelt->resdom->resname, uniqueAttrName) == 0)
+				tle = (TargetEntry *) lfirst(i);
+				if (strcmp(tle->resdom->resname, uniqueAttrName) == 0)
 					break;
 			}
 			if (i == NIL)
 				elog(ERROR, "All fields in the UNIQUE ON clause must appear in the target list");
 
-			foreach(s, sortlist)
-			{
-				SortClause *sortcl = lfirst(s);
+			sortlist = addTargetToSortList(tle, sortlist, targetlist, NULL);
+		}
+	}
 
-				if (sortcl->resdom == tlelt->resdom)
-					break;
-			}
-			if (s == NIL)
-			{
-				/* not a member of the sortclauses yet */
-				SortClause *sortcl = makeNode(SortClause);
+	return sortlist;
+}
 
-				sortcl->resdom = tlelt->resdom;
-				sortcl->opoid = any_ordering_op(tlelt->resdom->restype);
+/*
+ * addAllTargetsToSortList
+ *		Make sure all targets in the targetlist are in the ORDER BY list,
+ *		adding the not-yet-sorted ones to the end of the list.
+ *		This is typically used to help implement SELECT DISTINCT.
+ *
+ * Returns the updated ORDER BY list.
+ */
+List *
+addAllTargetsToSortList(List *sortlist, List *targetlist)
+{
+	List	   *i;
 
-				sortlist = lappend(sortlist, sortcl);
-			}
-		}
+	foreach(i, targetlist)
+	{
+		TargetEntry *tle = (TargetEntry *) lfirst(i);
+
+		sortlist = addTargetToSortList(tle, sortlist, targetlist, NULL);
 	}
+	return sortlist;
+}
+
+/*
+ * addTargetToSortList
+ *		If the given targetlist entry isn't already in the ORDER BY list,
+ *		add it to the end of the list, using the sortop with given name
+ *		or any available sort operator if opname == NULL.
+ *
+ * Returns the updated ORDER BY list.
+ */
+static List *
+addTargetToSortList(TargetEntry *tle, List *sortlist, List *targetlist,
+					char *opname)
+{
+	/* avoid making duplicate sortlist entries */
+	if (! exprIsInSortList(tle->expr, sortlist, targetlist))
+	{
+		SortClause *sortcl = makeNode(SortClause);
+
+		sortcl->tleSortGroupRef = assignSortGroupRef(tle, targetlist);
+
+		if (opname)
+			sortcl->sortop = oprid(oper(opname,
+										tle->resdom->restype,
+										tle->resdom->restype, false));
+		else
+			sortcl->sortop = any_ordering_op(tle->resdom->restype);
 
+		sortlist = lappend(sortlist, sortcl);
+	}
 	return sortlist;
 }
 
+/*
+ * assignSortGroupRef
+ *	  Assign the targetentry an unused ressortgroupref, if it doesn't
+ *	  already have one.  Return the assigned or pre-existing refnumber.
+ *
+ * 'tlist' is the targetlist containing (or to contain) the given targetentry.
+ */
+Index
+assignSortGroupRef(TargetEntry *tle, List *tlist)
+{
+	Index		maxRef;
+	List	   *l;
+
+	if (tle->resdom->ressortgroupref)		/* already has one? */
+		return tle->resdom->ressortgroupref;
+
+	/* easiest way to pick an unused refnumber: max used + 1 */
+	maxRef = 0;
+	foreach(l, tlist)
+	{
+		Index	ref = ((TargetEntry *) lfirst(l))->resdom->ressortgroupref;
+
+		if (ref > maxRef)
+			maxRef = ref;
+	}
+	tle->resdom->ressortgroupref = maxRef + 1;
+	return tle->resdom->ressortgroupref;
+}
+
+/*
+ * exprIsInSortList
+ *		Is the given expression already in the sortlist?
+ *		Note we will say 'yes' if it is equal() to any sortlist item,
+ *		even though that might be a different targetlist member.
+ *
+ * Works for both SortClause and GroupClause lists.
+ */
+static bool
+exprIsInSortList(Node *expr, List *sortList, List *targetList)
+{
+	List	   *i;
+
+	foreach(i, sortList)
+	{
+		SortClause *scl = (SortClause *) lfirst(i);
+
+		if (equal(expr, get_sortgroupclause_expr(scl, targetList)))
+			return true;
+	}
+	return false;
+}
+
 /* transformUnionClause()
  * Transform a UNION clause.
  * Note that the union clause is actually a fully-formed select structure.
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 344c87920bc7d585286adb1a6c3420d3f25a549f..4a227de2c5ba32620ffd39f6e2eac11121914c06 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.52 1999/08/16 02:08:59 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.53 1999/08/21 03:48:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -63,7 +63,6 @@ static int match_argtypes(int nargs,
 			   CandidateList function_typeids,
 			   CandidateList *candidates);
 static List *setup_tlist(char *attname, Oid relid);
-static List *setup_base_tlist(Oid typeid);
 static Oid *func_select_candidate(int nargs, Oid *input_typeids,
 					  CandidateList candidates);
 static int	agg_get_candidates(char *aggname, Oid typeId, CandidateList *candidates);
@@ -1312,7 +1311,7 @@ setup_tlist(char *attname, Oid relid)
  **		Build a tlist that extracts a base type from the tuple
  **		returned by the executor.
  */
-static List *
+List *
 setup_base_tlist(Oid typeid)
 {
 	TargetEntry *tle;
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 38464d343872b245ec09bea75330044ec322dac0..f88e936412ac352e7fb412c2b9ac85691aee18d4 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -6,7 +6,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.38 1999/07/17 20:17:38 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.39 1999/08/21 03:49:13 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -14,6 +14,7 @@
 
 #include "optimizer/clauses.h"
 #include "parser/parsetree.h"
+#include "parser/parse_clause.h"
 #include "rewrite/rewriteManip.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
@@ -533,25 +534,32 @@ AddNotQual(Query *parsetree, Node *qual)
 }
 
 
+/*
+ * Add all expressions used by the given GroupClause list to the
+ * parsetree's targetlist and groupclause list.
+ *
+ * tlist is the old targetlist associated with the input groupclauses.
+ *
+ * XXX shouldn't we be checking to see if there are already matching
+ * entries in parsetree->targetlist?
+ */
 void
 AddGroupClause(Query *parsetree, List *group_by, List *tlist)
 {
 	List	   *l;
-	List	   *tl;
-	GroupClause *groupclause;
-	TargetEntry *tle;
-	int			new_resno;
-
-	new_resno = length(parsetree->targetList);
 
 	foreach(l, group_by)
 	{
-		groupclause = (GroupClause *) copyObject(lfirst(l));
-		tle = NULL;
+		GroupClause *groupclause = (GroupClause *) copyObject(lfirst(l));
+		Index		refnumber = groupclause->tleSortGroupRef;
+		TargetEntry *tle = NULL;
+		List	   *tl;
+
+		/* Find and copy the groupclause's TLE in the old tlist */
 		foreach(tl, tlist)
 		{
-			if (((TargetEntry *) lfirst(tl))->resdom->resgroupref ==
-				groupclause->tleGroupref)
+			if (((TargetEntry *) lfirst(tl))->resdom->ressortgroupref ==
+				refnumber)
 			{
 				tle = (TargetEntry *) copyObject(lfirst(tl));
 				break;
@@ -560,10 +568,16 @@ AddGroupClause(Query *parsetree, List *group_by, List *tlist)
 		if (tle == NULL)
 			elog(ERROR, "AddGroupClause(): GROUP BY entry not found in rules targetlist");
 
-		tle->resdom->resno = ++new_resno;
+		/* The ressortgroupref number in the old tlist might be already
+		 * taken in the new tlist, so force assignment of a new number.
+		 */
+		tle->resdom->ressortgroupref = 0;
+		groupclause->tleSortGroupRef =
+			assignSortGroupRef(tle, parsetree->targetList);
+
+		/* Also need to set the resno and mark it resjunk. */
+		tle->resdom->resno = length(parsetree->targetList) + 1;
 		tle->resdom->resjunk = true;
-		tle->resdom->resgroupref = length(parsetree->groupClause) + 1;
-		groupclause->tleGroupref = tle->resdom->resgroupref;
 
 		parsetree->targetList = lappend(parsetree->targetList, tle);
 		parsetree->groupClause = lappend(parsetree->groupClause, groupclause);
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 8f365f2efbe9ae1b7c158d8e08eaedd11cb33ca0..008402e26f311473aaa545e10309258872e15761 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
  *			  out of it's tuple
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.21 1999/07/17 20:17:59 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.22 1999/08/21 03:48:53 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -41,6 +41,7 @@
 #include "postgres.h"
 #include "executor/spi.h"
 #include "optimizer/clauses.h"
+#include "optimizer/tlist.h"
 #include "utils/lsyscache.h"
 #include "catalog/pg_shadow.h"
 #include "catalog/pg_index.h"
@@ -1248,23 +1249,11 @@ get_rule_expr(QryHier *qh, int rt_index, Node *node, bool varprefix)
 		case T_GroupClause:
 			{
 				GroupClause *grp = (GroupClause *) node;
-				List	   *l;
-				TargetEntry *tle = NULL;
-
-				foreach(l, qh->query->targetList)
-				{
-					if (((TargetEntry *) lfirst(l))->resdom->resgroupref ==
-						grp->tleGroupref)
-					{
-						tle = (TargetEntry *) lfirst(l);
-						break;
-					}
-				}
-
-				if (tle == NULL)
-					elog(ERROR, "GROUP BY expression not found in targetlist");
+				Node *groupexpr;
 
-				return get_rule_expr(qh, rt_index, (Node *) tle, varprefix);
+				groupexpr = get_sortgroupclause_expr(grp,
+													 qh->query->targetList);
+				return get_rule_expr(qh, rt_index, groupexpr, varprefix);
 			}
 			break;
 
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 3b9cf8a8c9a7064699fb19481e924909d1a0f9d3..132f05266762c791a48398e23f3afefa6d3a1d30 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.33 1999/07/16 17:07:33 momjian Exp $
+ * $Id: execnodes.h,v 1.34 1999/08/21 03:49:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -572,6 +572,7 @@ typedef struct MaterialState
 typedef struct AggState
 {
 	CommonScanState csstate;	/* its first field is NodeTag */
+	List	   *aggs;			/* all Aggref nodes in targetlist & quals */
 	bool		agg_done;
 } AggState;
 
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ab22b737e48f4dfbe2f41a0fb2cc3a0b27efd00a..5a1d07ec3913bb0ec4c79778b0588617157a1bd5 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.77 1999/07/18 03:45:01 tgl Exp $
+ * $Id: parsenodes.h,v 1.78 1999/08/21 03:49:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,28 +48,29 @@ typedef struct Query
 	bool		hasAggs;		/* has aggregates in target list */
 	bool		hasSubLinks;	/* has subquery SubLink */
 
-	char	   *uniqueFlag;		/* NULL, '*', or Unique attribute name */
-	List	   *sortClause;		/* a list of SortClause's */
-
 	List	   *rtable;			/* list of range table entries */
 	List	   *targetList;		/* target list (of TargetEntry) */
-	Node	   *qual;			/* qualifications */
+	Node	   *qual;			/* qualifications applied to tuples */
 	List	   *rowMark;		/* list of RowMark entries */
 
-	List	   *groupClause;	/* list of columns to specified in GROUP
-								 * BY */
-	Node	   *havingQual;		/* qualification of each group */
+	char	   *uniqueFlag;		/* NULL, '*', or Unique attribute name */
+	List	   *sortClause;		/* a list of SortClause's */
 
-	List	   *intersectClause;
+	List	   *groupClause;	/* a list of GroupClause's */
+
+	Node	   *havingQual;		/* qualifications applied to groups */
 
+	List	   *intersectClause;
 	List	   *unionClause;	/* unions are linked under the previous
 								 * query */
+
 	Node	   *limitOffset;	/* # of result tuples to skip */
 	Node	   *limitCount;		/* # of result tuples to return */
 
 	/* internal to planner */
-	List	   *base_rel_list;	/* base relation list */
-	List	   *join_rel_list;	/* list of relation involved in joins */
+	List	   *base_rel_list;	/* list of base-relation RelOptInfos */
+	List	   *join_rel_list;	/* list of join-relation RelOptInfos */
+	List	   *query_pathkeys;	/* pathkeys for query_planner()'s result */
 } Query;
 
 
@@ -608,7 +609,7 @@ typedef struct InsertStmt
 	List	   *targetList;		/* the target list (of ResTarget) */
 	List	   *fromClause;		/* the from clause */
 	Node	   *whereClause;	/* qualifications */
-	List	   *groupClause;	/* group by clause */
+	List	   *groupClause;	/* GROUP BY clauses */
 	Node	   *havingClause;	/* having conditional-expression */
 	List	   *unionClause;	/* union subselect parameters */
 	bool		unionall;		/* union without unique sort */
@@ -652,7 +653,7 @@ typedef struct SelectStmt
 	List	   *targetList;		/* the target list (of ResTarget) */
 	List	   *fromClause;		/* the from clause */
 	Node	   *whereClause;	/* qualifications */
-	List	   *groupClause;	/* group by clause */
+	List	   *groupClause;	/* GROUP BY clauses */
 	Node	   *havingClause;	/* having conditional-expression */
 	List	   *intersectClause;
 	List	   *exceptClause;
@@ -950,25 +951,28 @@ typedef struct RangeTblEntry
 
 /*
  * SortClause -
- *	   used in the sort clause for retrieves and cursors
+ *	   representation of ORDER BY clauses
+ *
+ * tleSortGroupRef must match ressortgroupref of exactly one Resdom of the
+ * associated targetlist; that is the expression to be sorted (or grouped) by.
+ * sortop is the OID of the ordering operator.
  */
 typedef struct SortClause
 {
 	NodeTag		type;
-	Resdom	   *resdom;			/* attributes in tlist to be sorted */
-	Oid			opoid;			/* sort operators */
+	Index		tleSortGroupRef; /* reference into targetlist */
+	Oid			sortop;			/* the sort operator to use */
 } SortClause;
 
 /*
  * GroupClause -
- *	   used in the GROUP BY clause
+ *	   representation of GROUP BY clauses
+ *
+ * GroupClause is exactly like SortClause except for the nodetag value
+ * (and it's probably not even really necessary to have two different
+ * nodetags...).  We have routines that operate interchangeably on both.
  */
-typedef struct GroupClause
-{
-	NodeTag		type;
-	Oid			grpOpoid;		/* the sort operator to use */
-	Index		tleGroupref;	/* reference into targetlist */
-} GroupClause;
+typedef SortClause GroupClause;
 
 #define ROW_MARK_FOR_UPDATE		(1 << 0)
 #define ROW_ACL_FOR_UPDATE		(1 << 1)
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 532be5196bf8f4a877972f537be7d831db8bb255..095ee074d386bb53bd7c495694bb43367bc00b57 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: plannodes.h,v 1.29 1999/08/09 06:20:27 momjian Exp $
+ * $Id: plannodes.h,v 1.30 1999/08/21 03:49:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -232,7 +232,6 @@ typedef struct HashJoin
 typedef struct Agg
 {
 	Plan		plan;
-	List	   *aggs;
 	AggState   *aggstate;
 } Agg;
 
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 2c4d0ffaa72a96c95ec1c0b1774e884f794b73ad..4eea81446b2e23877c9a03774a56c013278d3b24 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: primnodes.h,v 1.33 1999/08/16 02:17:39 tgl Exp $
+ * $Id: primnodes.h,v 1.34 1999/08/21 03:49:09 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -25,15 +25,33 @@
 /* ----------------
  * Resdom (Result Domain)
  *		resno			- attribute number
- *		restype			- type of the resdom
- *		restypmod		- type-specific modifier of the result
+ *		restype			- type of the value
+ *		restypmod		- type-specific modifier of the value
  *		resname			- name of the resdom (could be NULL)
+ *		ressortgroupref	- nonzero if referenced by a sort/group clause
  *		reskey			- order of key in a sort (for those > 0)
- *		reskeyop		- sort operator Oid
- *		resgroupref		- set to nonzero if referenced from a group by clause
+ *		reskeyop		- sort operator's regproc Oid
  *		resjunk			- set to true to eliminate the attribute
  *						  from final target list
  *
+ * Notes:
+ * ressortgroupref is the parse/plan-time 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
+ * ressortgroupref --- but there is no particular meaning to the nonzero
+ * values, except as tags.  (For example, one must not assume that lower
+ * 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
+ * regproc OID.  reskeyop will be zero in non-sort-key items.
+ *
+ * Both reskey and reskeyop are typically zero during parse/plan stages.
+ * The executor does not pay any attention to ressortgroupref.
  * ----------------
  */
 typedef struct Resdom
@@ -43,9 +61,9 @@ typedef struct Resdom
 	Oid			restype;
 	int32		restypmod;
 	char	   *resname;
+	Index		ressortgroupref;
 	Index		reskey;
 	Oid			reskeyop;
-	Index		resgroupref;
 	bool		resjunk;
 } Resdom;
 
@@ -275,7 +293,8 @@ typedef struct Iter
  *		basetype		- base type Oid of the aggregate
  *		aggtype			- type Oid of final result of the aggregate
  *		target			- attribute or expression we are aggregating on
- *		aggno			- index to ecxt_values
+ *		usenulls		- TRUE to accept null values as inputs
+ *		aggno			- workspace for nodeAgg.c executor
  * ----------------
  */
 typedef struct Aggref
@@ -285,8 +304,8 @@ typedef struct Aggref
 	Oid			basetype;
 	Oid			aggtype;
 	Node	   *target;
-	int			aggno;
 	bool		usenulls;
+	int			aggno;
 } Aggref;
 
 /* ----------------
diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h
index b0ec64c3e32e622a110afc793a6dc52bc12fe23e..58da94368c95d59677580f03a5678faa0dfa5d64 100644
--- a/src/include/optimizer/paths.h
+++ b/src/include/optimizer/paths.h
@@ -7,7 +7,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: paths.h,v 1.34 1999/08/16 02:17:45 tgl Exp $
+ * $Id: paths.h,v 1.35 1999/08/21 03:49:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -56,11 +56,15 @@ typedef enum
 
 extern PathKeysComparison compare_pathkeys(List *keys1, List *keys2);
 extern bool pathkeys_contained_in(List *keys1, List *keys2);
-extern Path *get_cheapest_path_for_pathkeys(List *paths, List *pathkeys);
+extern Path *get_cheapest_path_for_pathkeys(List *paths, List *pathkeys,
+											bool indexpaths_only);
 extern List *build_index_pathkeys(Query *root, RelOptInfo *rel,
 								  RelOptInfo *index);
 extern List *build_join_pathkeys(List *outer_pathkeys,
 								 List *join_rel_tlist, List *joinclauses);
+extern bool commute_pathkeys(List *pathkeys);
+extern List *make_pathkeys_for_sortclauses(List *sortclauses,
+										   List *tlist);
 extern List *find_mergeclauses_for_pathkeys(List *pathkeys,
 											List *restrictinfos);
 extern List *make_pathkeys_for_mergeclauses(List *mergeclauses,
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index 0ba017471d17ca4099738c84b2178417362715d4..38ff367384be5b1d08089b1664dcd36d9fabbabb 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: planmain.h,v 1.30 1999/08/09 00:56:04 tgl Exp $
+ * $Id: planmain.h,v 1.31 1999/08/21 03:49:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,7 +33,7 @@ extern Sort *make_sort(List *tlist, Oid nonameid, Plan *lefttree,
 		  int keycount);
 extern Agg *make_agg(List *tlist, Plan *lefttree);
 extern Group *make_group(List *tlist, bool tuplePerGroup, int ngrp,
-		   AttrNumber *grpColIdx, Sort *lefttree);
+		   AttrNumber *grpColIdx, Plan *lefttree);
 extern Unique *make_unique(List *tlist, Plan *lefttree, char *uniqueAttr);
 
 /*
@@ -54,9 +54,14 @@ extern void replace_tlist_with_subplan_refs(List *tlist,
 								Index subvarno,
 								List *subplanTargetList);
 extern bool set_agg_tlist_references(Agg *aggNode);
+extern List *pull_agg_clause(Node *clause);
 extern void check_having_for_ungrouped_vars(Node *clause,
 								List *groupClause,
 								List *targetList);
+
+/*
+ * prep/prepkeyset.c
+ */
 extern void transformKeySetQuery(Query *origNode);
 
 #endif	 /* PLANMAIN_H */
diff --git a/src/include/optimizer/tlist.h b/src/include/optimizer/tlist.h
index fb08b708352ae1f50da1768624a2607acf1c9734..f0ddb9ac9dcc4b406ae668384a043042f834813f 100644
--- a/src/include/optimizer/tlist.h
+++ b/src/include/optimizer/tlist.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: tlist.h,v 1.20 1999/08/16 02:17:45 tgl Exp $
+ * $Id: tlist.h,v 1.21 1999/08/21 03:49:15 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,16 +21,17 @@ extern void add_var_to_tlist(RelOptInfo *rel, Var *var);
 extern TargetEntry *create_tl_element(Var *var, int resdomno);
 extern List *get_actual_tlist(List *tlist);
 extern Resdom *tlist_member(Var *var, List *tlist);
-extern Resdom *tlist_resdom(List *tlist, Resdom *resnode);
 
 extern TargetEntry *match_varid(Var *test_var, List *tlist);
 extern List *new_unsorted_tlist(List *targetlist);
 extern List *copy_vars(List *target, List *source);
 extern List *flatten_tlist(List *tlist);
-extern List *flatten_tlist_vars(List *full_tlist,
-				   List *flat_tlist);
+extern List *add_to_flat_tlist(List *tlist, List *vars);
+extern List *unflatten_tlist(List *full_tlist,
+							 List *flat_tlist);
 
 extern Var *get_expr(TargetEntry *tle);
-extern Var *get_groupclause_expr(GroupClause *groupClause, List *targetList);
+extern Node *get_sortgroupclause_expr(SortClause *sortClause,
+									  List *targetList);
 
 #endif	 /* TLIST_H */
diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h
index 3b1aafa07d923e709a14b6aaa917d7cb78a7ec47..6b30301ea0b218fd71f69edc9ba56c05dc5d1e3d 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.12 1999/07/19 00:26:16 tgl Exp $
+ * $Id: parse_clause.h,v 1.13 1999/08/21 03:49:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,8 +20,11 @@ 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,
-					List *orderlist, List *sortClause,
-					List *targetlist, char *uniqueFlag);
+								  List *targetlist);
+extern List *transformSortClause(ParseState *pstate, List *orderlist,
+								 List *targetlist, char *uniqueFlag);
+
+extern List *addAllTargetsToSortList(List *sortlist, List *targetlist);
+extern Index assignSortGroupRef(TargetEntry *tle, List *tlist);
+
 #endif	 /* PARSE_CLAUSE_H */
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index 38626290cb1085858d23b83f5425fc17f4890989..1fdb8f9890da004a3da3b2283b0f2e4b91bb4715 100644
--- a/src/include/parser/parse_func.h
+++ b/src/include/parser/parse_func.h
@@ -6,7 +6,7 @@
  *
  * Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_func.h,v 1.18 1999/07/15 23:04:02 momjian Exp $
+ * $Id: parse_func.h,v 1.19 1999/08/21 03:49:17 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -45,6 +45,8 @@ extern Node *ParseNestedFuncOrColumn(ParseState *pstate, Attr *attr,
 extern Node *ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
 				  int *curr_resno, int precedence);
 
+extern List *setup_base_tlist(Oid typeid);
+
 extern void func_error(char *caller, char *funcname,
 					   int nargs, Oid *argtypes, char *msg);