diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index f0aabc9dc8d6b095c3266a71caa81f90ac30eb28..3d916949243dac0ae658a92e9ac1703807ae2f43 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994-5, Regents of the University of California
  *
- * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.89 2002/10/14 04:26:54 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.90 2002/11/06 00:00:43 tgl Exp $
  *
  */
 
@@ -275,7 +275,21 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
 			pname = "Group";
 			break;
 		case T_Agg:
-			pname = "Aggregate";
+			switch (((Agg *) plan)->aggstrategy)
+			{
+				case AGG_PLAIN:
+					pname = "Aggregate";
+					break;
+				case AGG_SORTED:
+					pname = "GroupAggregate";
+					break;
+				case AGG_HASHED:
+					pname = "HashAggregate";
+					break;
+				default:
+					pname = "Aggregate ???";
+					break;
+			}
 			break;
 		case T_Unique:
 			pname = "Unique";
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index bf4a9bbbdaaaf63096635df5881ddea9dc20dcb5..7714a68090941079bc7536a35b26faebfefd06d2 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -46,7 +46,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.90 2002/11/01 19:33:09 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.91 2002/11/06 00:00:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -58,6 +58,7 @@
 #include "catalog/pg_operator.h"
 #include "executor/executor.h"
 #include "executor/nodeAgg.h"
+#include "executor/nodeGroup.h"
 #include "miscadmin.h"
 #include "optimizer/clauses.h"
 #include "parser/parse_coerce.h"
@@ -159,6 +160,7 @@ typedef struct AggStatePerAggData
 static void initialize_aggregate(AggStatePerAgg peraggstate);
 static void advance_transition_function(AggStatePerAgg peraggstate,
 							Datum newVal, bool isNull);
+static void advance_aggregates(AggState *aggstate, ExprContext *econtext);
 static void process_sorted_aggregate(AggState *aggstate,
 						 AggStatePerAgg peraggstate);
 static void finalize_aggregate(AggStatePerAgg peraggstate,
@@ -313,6 +315,62 @@ advance_transition_function(AggStatePerAgg peraggstate,
 	peraggstate->transValueIsNull = fcinfo.isnull;
 }
 
+/*
+ * Advance all the aggregates for one input tuple.  The input tuple
+ * has been stored in econtext->ecxt_scantuple, so that it is accessible
+ * to ExecEvalExpr.
+ *
+ * When called, CurrentMemoryContext should be the per-query context.
+ */
+static void
+advance_aggregates(AggState *aggstate, ExprContext *econtext)
+{
+	MemoryContext oldContext;
+	int			aggno;
+
+	/*
+	 * Clear and select the current working context for evaluation
+	 * of the input expressions and transition functions at this
+	 * input tuple.
+	 */
+	econtext->ecxt_per_tuple_memory = aggstate->agg_cxt[aggstate->which_cxt];
+	ResetExprContext(econtext);
+	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	for (aggno = 0; aggno < aggstate->numaggs; aggno++)
+	{
+		AggStatePerAgg peraggstate = &aggstate->peragg[aggno];
+		Aggref	   *aggref = peraggstate->aggref;
+		Datum		newVal;
+		bool		isNull;
+
+		newVal = ExecEvalExpr(aggref->target, econtext, &isNull, NULL);
+
+		if (aggref->aggdistinct)
+		{
+			/* in DISTINCT mode, we may ignore nulls */
+			if (isNull)
+				continue;
+			/* putdatum has to be called in per-query context */
+			MemoryContextSwitchTo(oldContext);
+			tuplesort_putdatum(peraggstate->sortstate, newVal, isNull);
+			MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+		}
+		else
+		{
+			advance_transition_function(peraggstate, newVal, isNull);
+		}
+	}
+
+	/*
+	 * Make the other context current so that these transition
+	 * results are preserved.
+	 */
+	aggstate->which_cxt = 1 - aggstate->which_cxt;
+
+	MemoryContextSwitchTo(oldContext);
+}
+
 /*
  * Run the transition function for a DISTINCT aggregate.  This is called
  * after we have completed entering all the input values into the sort
@@ -448,23 +506,18 @@ finalize_aggregate(AggStatePerAgg peraggstate,
 }
 
 
-/* ---------------------------------------
- *
+/*
  * ExecAgg -
  *
  *	  ExecAgg receives tuples from its outer subplan and aggregates over
  *	  the appropriate attribute for each aggregate function use (Aggref
  *	  node) appearing in the targetlist or qual of the node.  The number
- *	  of tuples to aggregate over depends on whether a GROUP BY clause is
- *	  present.	We can produce an aggregate result row per group, or just
- *	  one for the whole query.	The value of each aggregate is stored in
- *	  the expression context to be used when ExecProject evaluates the
- *	  result tuple.
- *
- *	  If the outer subplan is a Group node, ExecAgg returns as many tuples
- *	  as there are groups.
- *
- * ------------------------------------------
+ *	  of tuples to aggregate over depends on whether grouped or plain
+ *	  aggregation is selected.  In grouped aggregation, we produce a result
+ *	  row for each group; in plain aggregation there's a single result row
+ *	  for the whole query.  In either case, the value of each aggregate is
+ *	  stored in the expression context to be used when ExecProject evaluates
+ *	  the result tuple.
  */
 TupleTableSlot *
 ExecAgg(Agg *node)
@@ -478,10 +531,10 @@ ExecAgg(Agg *node)
 	bool	   *aggnulls;
 	AggStatePerAgg peragg;
 	MemoryContext oldContext;
+	TupleTableSlot *outerslot;
+	TupleTableSlot *firstSlot;
 	TupleTableSlot *resultSlot;
-	HeapTuple	inputTuple;
 	int			aggno;
-	bool		isNull;
 
 	/*
 	 * get state info from node
@@ -494,6 +547,7 @@ ExecAgg(Agg *node)
 	aggnulls = econtext->ecxt_aggnulls;
 	projInfo = aggstate->csstate.cstate.cs_ProjInfo;
 	peragg = aggstate->peragg;
+	firstSlot = aggstate->csstate.css_ScanTupleSlot;
 
 	/*
 	 * We loop retrieving groups until we find one matching
@@ -504,6 +558,31 @@ ExecAgg(Agg *node)
 		if (aggstate->agg_done)
 			return NULL;
 
+		/*
+		 * If we don't already have the first tuple of the new group,
+		 * fetch it from the outer plan.
+		 */
+		if (aggstate->grp_firstTuple == NULL)
+		{
+			outerslot = ExecProcNode(outerPlan, (Plan *) node);
+			if (!TupIsNull(outerslot))
+			{
+				/*
+				 * Make a copy of the first input tuple; we will use this
+				 * for comparisons (in group mode) and for projection.
+				 */
+				aggstate->grp_firstTuple = heap_copytuple(outerslot->val);
+			}
+			else
+			{
+				/* outer plan produced no tuples at all */
+				aggstate->agg_done = true;
+				/* If we are grouping, we should produce no tuples too */
+				if (node->aggstrategy != AGG_PLAIN)
+					return NULL;
+			}
+		}
+
 		/*
 		 * Clear the per-output-tuple context for each group
 		 */
@@ -519,74 +598,61 @@ ExecAgg(Agg *node)
 			initialize_aggregate(peraggstate);
 		}
 
-		inputTuple = NULL;		/* no saved input tuple yet */
-
-		/*
-		 * for each tuple from the outer plan, update all the aggregates
-		 */
-		for (;;)
+		if (aggstate->grp_firstTuple != NULL)
 		{
-			TupleTableSlot *outerslot;
+			/*
+			 * Store the copied first input tuple in the tuple table slot
+			 * reserved for it.  The tuple will be deleted when it is
+			 * cleared from the slot.
+			 */
+			ExecStoreTuple(aggstate->grp_firstTuple,
+						   firstSlot,
+						   InvalidBuffer,
+						   true);
+			aggstate->grp_firstTuple = NULL; /* don't keep two pointers */
 
-			outerslot = ExecProcNode(outerPlan, (Plan *) node);
-			if (TupIsNull(outerslot))
-				break;
-			econtext->ecxt_scantuple = outerslot;
+			/* set up for first advance_aggregates call */
+			econtext->ecxt_scantuple = firstSlot;
 
 			/*
-			 * Clear and select the current working context for evaluation
-			 * of the input expressions and transition functions at this
-			 * input tuple.
+			 * Process each outer-plan tuple, and then fetch the next one,
+			 * until we exhaust the outer plan or cross a group boundary.
 			 */
-			econtext->ecxt_per_tuple_memory =
-				aggstate->agg_cxt[aggstate->which_cxt];
-			ResetExprContext(econtext);
-			oldContext =
-				MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-			for (aggno = 0; aggno < aggstate->numaggs; aggno++)
+			for (;;)
 			{
-				AggStatePerAgg peraggstate = &peragg[aggno];
-				Aggref	   *aggref = peraggstate->aggref;
-				Datum		newVal;
-
-				newVal = ExecEvalExpr(aggref->target, econtext,
-									  &isNull, NULL);
+				advance_aggregates(aggstate, econtext);
 
-				if (aggref->aggdistinct)
+				outerslot = ExecProcNode(outerPlan, (Plan *) node);
+				if (TupIsNull(outerslot))
 				{
-					/* in DISTINCT mode, we may ignore nulls */
-					if (isNull)
-						continue;
-					/* putdatum has to be called in per-query context */
-					MemoryContextSwitchTo(oldContext);
-					tuplesort_putdatum(peraggstate->sortstate,
-									   newVal, isNull);
-					MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+					/* no more outer-plan tuples available */
+					aggstate->agg_done = true;
+					break;
 				}
-				else
+				/* set up for next advance_aggregates call */
+				econtext->ecxt_scantuple = outerslot;
+
+				/*
+				 * If we are grouping, check whether we've crossed a group
+				 * boundary.
+				 */
+				if (node->aggstrategy == AGG_SORTED)
 				{
-					advance_transition_function(peraggstate,
-												newVal, isNull);
+					if (!execTuplesMatch(firstSlot->val,
+										 outerslot->val,
+										 firstSlot->ttc_tupleDescriptor,
+										 node->numCols, node->grpColIdx,
+										 aggstate->eqfunctions,
+										 aggstate->agg_cxt[aggstate->which_cxt]))
+					{
+						/*
+						 * Save the first input tuple of the next group.
+						 */
+						aggstate->grp_firstTuple = heap_copytuple(outerslot->val);
+						break;
+					}
 				}
 			}
-
-			/*
-			 * Make the other context current so that these transition
-			 * results are preserved.
-			 */
-			aggstate->which_cxt = 1 - aggstate->which_cxt;
-
-			MemoryContextSwitchTo(oldContext);
-
-			/*
-			 * Keep a copy of the first input tuple for the projection.
-			 * (We only need one since only the GROUP BY columns in it can
-			 * be referenced, and these will be the same for all tuples
-			 * aggregated over.)
-			 */
-			if (!inputTuple)
-				inputTuple = heap_copytuple(outerslot->val);
 		}
 
 		/*
@@ -626,77 +692,50 @@ ExecAgg(Agg *node)
 		}
 
 		/*
-		 * If the outerPlan is a Group node, we will reach here after each
-		 * group.  We are not done unless the Group node is done (a little
-		 * ugliness here while we reach into the Group's state to find
-		 * out). Furthermore, when grouping we return nothing at all
-		 * unless we had some input tuple(s).  By the nature of Group,
-		 * there are no empty groups, so if we get here with no input the
-		 * whole scan is empty.
+		 * If we have no first tuple (ie, the outerPlan didn't return
+		 * anything), create a dummy all-nulls input tuple for use by
+		 * ExecProject. 99.44% of the time this is a waste of cycles,
+		 * because ordinarily the projected output tuple's targetlist
+		 * cannot contain any direct (non-aggregated) references to
+		 * input columns, so the dummy tuple will not be referenced.
+		 * However there are special cases where this isn't so --- in
+		 * particular an UPDATE involving an aggregate will have a
+		 * targetlist reference to ctid.  We need to return a null for
+		 * ctid in that situation, not coredump.
 		 *
-		 * If the outerPlan isn't a Group, we are done when we get here, and
-		 * we will emit a (single) tuple even if there were no input
-		 * tuples.
+		 * The values returned for the aggregates will be the initial
+		 * values of the transition functions.
 		 */
-		if (IsA(outerPlan, Group))
+		if (TupIsNull(firstSlot))
 		{
-			/* aggregation over groups */
-			aggstate->agg_done = ((Group *) outerPlan)->grpstate->grp_done;
-			/* check for no groups */
-			if (inputTuple == NULL)
-				return NULL;
-		}
-		else
-		{
-			aggstate->agg_done = true;
+			TupleDesc	tupType;
 
-			/*
-			 * If inputtuple==NULL (ie, the outerPlan didn't return
-			 * anything), create a dummy all-nulls input tuple for use by
-			 * ExecProject. 99.44% of the time this is a waste of cycles,
-			 * because ordinarily the projected output tuple's targetlist
-			 * cannot contain any direct (non-aggregated) references to
-			 * input columns, so the dummy tuple will not be referenced.
-			 * However there are special cases where this isn't so --- in
-			 * particular an UPDATE involving an aggregate will have a
-			 * targetlist reference to ctid.  We need to return a null for
-			 * ctid in that situation, not coredump.
-			 *
-			 * The values returned for the aggregates will be the initial
-			 * values of the transition functions.
-			 */
-			if (inputTuple == NULL)
+			/* Should only happen in non-grouped mode */
+			Assert(node->aggstrategy == AGG_PLAIN);
+			Assert(aggstate->agg_done);
+
+			tupType = firstSlot->ttc_tupleDescriptor;
+			/* watch out for zero-column input tuples, though... */
+			if (tupType && tupType->natts > 0)
 			{
-				TupleDesc	tupType;
+				HeapTuple	nullsTuple;
 				Datum	   *dvalues;
 				char	   *dnulls;
 
-				tupType = aggstate->csstate.css_ScanTupleSlot->ttc_tupleDescriptor;
-				/* watch out for null input tuples, though... */
-				if (tupType && tupType->natts > 0)
-				{
-					dvalues = (Datum *) palloc(sizeof(Datum) * tupType->natts);
-					dnulls = (char *) palloc(sizeof(char) * tupType->natts);
-					MemSet(dvalues, 0, sizeof(Datum) * tupType->natts);
-					MemSet(dnulls, 'n', sizeof(char) * tupType->natts);
-					inputTuple = heap_formtuple(tupType, dvalues, dnulls);
-					pfree(dvalues);
-					pfree(dnulls);
-				}
+				dvalues = (Datum *) palloc(sizeof(Datum) * tupType->natts);
+				dnulls = (char *) palloc(sizeof(char) * tupType->natts);
+				MemSet(dvalues, 0, sizeof(Datum) * tupType->natts);
+				MemSet(dnulls, 'n', sizeof(char) * tupType->natts);
+				nullsTuple = heap_formtuple(tupType, dvalues, dnulls);
+				ExecStoreTuple(nullsTuple,
+							   firstSlot,
+							   InvalidBuffer,
+							   true);
+				pfree(dvalues);
+				pfree(dnulls);
 			}
 		}
 
-		/*
-		 * Store the representative input tuple in the tuple table slot
-		 * reserved for it.  The tuple will be deleted when it is cleared
-		 * from the slot.
-		 */
-		ExecStoreTuple(inputTuple,
-					   aggstate->csstate.css_ScanTupleSlot,
-					   InvalidBuffer,
-					   true);
-		econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot;
-
 		/*
 		 * Do projection and qual check in the per-output-tuple context.
 		 */
@@ -707,6 +746,7 @@ ExecAgg(Agg *node)
 		 * representative input tuple.	Store it in the result tuple slot.
 		 * Note we do not support aggregates returning sets ...
 		 */
+		econtext->ecxt_scantuple = firstSlot;
 		resultSlot = ExecProject(projInfo, NULL);
 
 		/*
@@ -748,6 +788,8 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
 	 */
 	aggstate = makeNode(AggState);
 	node->aggstate = aggstate;
+	aggstate->eqfunctions = NULL;
+	aggstate->grp_firstTuple = NULL;
 	aggstate->agg_done = false;
 
 	/*
@@ -765,13 +807,12 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
 	if (numaggs <= 0)
 	{
 		/*
-		 * This used to be treated as an error, but we can't do that
-		 * anymore because constant-expression simplification could
-		 * optimize away all of the Aggrefs in the targetlist and qual.
-		 * So, just make a debug note, and force numaggs positive so that
-		 * palloc()s below don't choke.
+		 * This is not an error condition: we might be using the Agg node just
+		 * to do hash-based grouping.  Even in the regular case,
+		 * constant-expression simplification could optimize away all of the
+		 * Aggrefs in the targetlist and qual.  So keep going, but force local
+		 * copy of numaggs positive so that palloc()s below don't choke.
 		 */
-		elog(DEBUG1, "ExecInitAgg: could not find any aggregate functions");
 		numaggs = 1;
 	}
 
@@ -843,6 +884,17 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
 	ExecAssignResultTypeFromTL((Plan *) node, &aggstate->csstate.cstate);
 	ExecAssignProjectionInfo((Plan *) node, &aggstate->csstate.cstate);
 
+	/*
+	 * If we are grouping, precompute fmgr lookup data for inner loop
+	 */
+	if (node->numCols > 0)
+	{
+		aggstate->eqfunctions =
+			execTuplesMatchPrepare(ExecGetScanType(&aggstate->csstate),
+								   node->numCols,
+								   node->grpColIdx);
+	}
+
 	/*
 	 * Perform lookups of aggregate function info, and initialize the
 	 * unchanging fields of the per-agg data
@@ -1024,6 +1076,11 @@ ExecEndAgg(Agg *node)
 
 	/* clean up tuple table */
 	ExecClearTuple(aggstate->csstate.css_ScanTupleSlot);
+	if (aggstate->grp_firstTuple != NULL)
+	{
+		heap_freetuple(aggstate->grp_firstTuple);
+		aggstate->grp_firstTuple = NULL;
+	}
 }
 
 void
@@ -1033,6 +1090,11 @@ ExecReScanAgg(Agg *node, ExprContext *exprCtxt, Plan *parent)
 	ExprContext *econtext = aggstate->csstate.cstate.cs_ExprContext;
 
 	aggstate->agg_done = false;
+	if (aggstate->grp_firstTuple != NULL)
+	{
+		heap_freetuple(aggstate->grp_firstTuple);
+		aggstate->grp_firstTuple = NULL;
+	}
 	MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * aggstate->numaggs);
 	MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * aggstate->numaggs);
 
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index 5d7f6a699246c4a7b7570502a75937a44f6f5e48..662c3d4798ca5c2a9b4b468b3246734feccefdb4 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -15,7 +15,7 @@
  *	  locate group boundaries.
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.47 2002/06/20 20:29:28 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.48 2002/11/06 00:00:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,147 +31,14 @@
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
-static TupleTableSlot *ExecGroupEveryTuple(Group *node);
-static TupleTableSlot *ExecGroupOneTuple(Group *node);
 
-/* ---------------------------------------
+/*
  *	 ExecGroup -
  *
- *		There are two modes in which tuples are returned by ExecGroup. If
- *		tuplePerGroup is TRUE, every tuple from the same group will be
- *		returned, followed by a NULL at the end of each group. This is
- *		useful for Agg node which needs to aggregate over tuples of the same
- *		group. (eg. SELECT salary, count(*) FROM emp GROUP BY salary)
- *
- *		If tuplePerGroup is FALSE, only one tuple per group is returned. The
- *		tuple returned contains only the group columns. NULL is returned only
- *		at the end when no more groups are present. This is useful when
- *		the query does not involve aggregates. (eg. SELECT salary FROM emp
- *		GROUP BY salary)
- * ------------------------------------------
+ *		Return one tuple for each group of matching input tuples.
  */
 TupleTableSlot *
 ExecGroup(Group *node)
-{
-	if (node->tuplePerGroup)
-		return ExecGroupEveryTuple(node);
-	else
-		return ExecGroupOneTuple(node);
-}
-
-/*
- * ExecGroupEveryTuple -
- *	 return every tuple with a NULL between each group
- */
-static TupleTableSlot *
-ExecGroupEveryTuple(Group *node)
-{
-	GroupState *grpstate;
-	EState	   *estate;
-	ExprContext *econtext;
-	TupleDesc	tupdesc;
-	HeapTuple	outerTuple = NULL;
-	HeapTuple	firsttuple;
-	TupleTableSlot *outerslot;
-	ProjectionInfo *projInfo;
-	TupleTableSlot *resultSlot;
-
-	/*
-	 * get state info from node
-	 */
-	grpstate = node->grpstate;
-	if (grpstate->grp_done)
-		return NULL;
-	estate = node->plan.state;
-	econtext = grpstate->csstate.cstate.cs_ExprContext;
-	tupdesc = ExecGetScanType(&grpstate->csstate);
-
-	/*
-	 * We need not call ResetExprContext here because execTuplesMatch will
-	 * reset the per-tuple memory context once per input tuple.
-	 */
-
-	/* if we haven't returned first tuple of a new group yet ... */
-	if (grpstate->grp_useFirstTuple)
-	{
-		grpstate->grp_useFirstTuple = FALSE;
-
-		/*
-		 * note we rely on subplan to hold ownership of the tuple for as
-		 * long as we need it; we don't copy it.
-		 */
-		ExecStoreTuple(grpstate->grp_firstTuple,
-					   grpstate->csstate.css_ScanTupleSlot,
-					   InvalidBuffer, false);
-	}
-	else
-	{
-		outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
-		if (TupIsNull(outerslot))
-		{
-			grpstate->grp_done = TRUE;
-			return NULL;
-		}
-		outerTuple = outerslot->val;
-
-		firsttuple = grpstate->grp_firstTuple;
-		if (firsttuple == NULL)
-		{
-			/* this should occur on the first call only */
-			grpstate->grp_firstTuple = heap_copytuple(outerTuple);
-		}
-		else
-		{
-			/*
-			 * Compare with first tuple and see if this tuple is of the
-			 * same group.
-			 */
-			if (!execTuplesMatch(firsttuple, outerTuple,
-								 tupdesc,
-								 node->numCols, node->grpColIdx,
-								 grpstate->eqfunctions,
-								 econtext->ecxt_per_tuple_memory))
-			{
-				/*
-				 * No; save the tuple to return it next time, and return
-				 * NULL
-				 */
-				grpstate->grp_useFirstTuple = TRUE;
-				heap_freetuple(firsttuple);
-				grpstate->grp_firstTuple = heap_copytuple(outerTuple);
-
-				return NULL;	/* signifies the end of the group */
-			}
-		}
-
-		/*
-		 * note we rely on subplan to hold ownership of the tuple for as
-		 * long as we need it; we don't copy it.
-		 */
-		ExecStoreTuple(outerTuple,
-					   grpstate->csstate.css_ScanTupleSlot,
-					   InvalidBuffer, false);
-	}
-
-	/*
-	 * form a projection tuple, store it in the result tuple slot and
-	 * return it.
-	 */
-	projInfo = grpstate->csstate.cstate.cs_ProjInfo;
-
-	econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
-	resultSlot = ExecProject(projInfo, NULL);
-
-	return resultSlot;
-}
-
-/*
- * ExecGroupOneTuple -
- *	  returns one tuple per group, a NULL at the end when there are no more
- *	  tuples.
- */
-static TupleTableSlot *
-ExecGroupOneTuple(Group *node)
 {
 	GroupState *grpstate;
 	EState	   *estate;
@@ -198,10 +65,11 @@ ExecGroupOneTuple(Group *node)
 	 * reset the per-tuple memory context once per input tuple.
 	 */
 
+	/* If we don't already have first tuple of group, fetch it */
+	/* this should occur on the first call only */
 	firsttuple = grpstate->grp_firstTuple;
 	if (firsttuple == NULL)
 	{
-		/* this should occur on the first call only */
 		outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
 		if (TupIsNull(outerslot))
 		{
@@ -213,7 +81,7 @@ ExecGroupOneTuple(Group *node)
 	}
 
 	/*
-	 * find all tuples that belong to a group
+	 * Scan over all tuples that belong to this group
 	 */
 	for (;;)
 	{
@@ -239,22 +107,18 @@ ExecGroupOneTuple(Group *node)
 	}
 
 	/*
-	 * form a projection tuple, store it in the result tuple slot and
-	 * return it.
-	 */
-	projInfo = grpstate->csstate.cstate.cs_ProjInfo;
-
-	/*
-	 * note we rely on subplan to hold ownership of the tuple for as long
-	 * as we need it; we don't copy it.
+	 * form a projection tuple based on the (copied) first tuple of the
+	 * group, and store it in the result tuple slot.
 	 */
 	ExecStoreTuple(firsttuple,
 				   grpstate->csstate.css_ScanTupleSlot,
-				   InvalidBuffer, false);
+				   InvalidBuffer,
+				   false);
 	econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
+	projInfo = grpstate->csstate.cstate.cs_ProjInfo;
 	resultSlot = ExecProject(projInfo, NULL);
 
-	/* save outerTuple if we are not done yet */
+	/* save first tuple of next group, if we are not done yet */
 	if (!grpstate->grp_done)
 	{
 		heap_freetuple(firsttuple);
@@ -386,14 +250,14 @@ ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
 }
 
 /*****************************************************************************
- *		Code shared with nodeUnique.c
+ *		Code shared with nodeUnique.c and nodeAgg.c
  *****************************************************************************/
 
 /*
  * execTuplesMatch
  *		Return true if two tuples match in all the indicated fields.
- *		This is used to detect group boundaries in nodeGroup, and to
- *		decide whether two tuples are distinct or not in nodeUnique.
+ *		This is used to detect group boundaries in nodeGroup and nodeAgg,
+ *		and to decide whether two tuples are distinct or not in nodeUnique.
  *
  * tuple1, tuple2: the tuples to compare
  * tupdesc: tuple descriptor applying to both tuples
@@ -425,7 +289,8 @@ execTuplesMatch(HeapTuple tuple1,
 	 * We cannot report a match without checking all the fields, but we
 	 * can report a non-match as soon as we find unequal fields.  So,
 	 * start comparing at the last field (least significant sort key).
-	 * That's the most likely to be different...
+	 * That's the most likely to be different if we are dealing with
+	 * sorted input.
 	 */
 	result = true;
 
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 5fff2f762ab56eeb21afcd196dfd572332a72578..0438e0ce6096d5d4060846d3c454fe3f48eb49cc 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.214 2002/10/14 22:14:34 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.215 2002/11/06 00:00:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -497,10 +497,10 @@ _copyGroup(Group *from)
 
 	CopyPlanFields((Plan *) from, (Plan *) newnode);
 
-	newnode->tuplePerGroup = from->tuplePerGroup;
 	newnode->numCols = from->numCols;
 	newnode->grpColIdx = palloc(from->numCols * sizeof(AttrNumber));
-	memcpy(newnode->grpColIdx, from->grpColIdx, from->numCols * sizeof(AttrNumber));
+	memcpy(newnode->grpColIdx, from->grpColIdx,
+		   from->numCols * sizeof(AttrNumber));
 
 	return newnode;
 }
@@ -516,6 +516,15 @@ _copyAgg(Agg *from)
 
 	CopyPlanFields((Plan *) from, (Plan *) newnode);
 
+	newnode->aggstrategy = from->aggstrategy;
+	newnode->numCols = from->numCols;
+	if (from->numCols > 0)
+	{
+		newnode->grpColIdx = palloc(from->numCols * sizeof(AttrNumber));
+		memcpy(newnode->grpColIdx, from->grpColIdx,
+			   from->numCols * sizeof(AttrNumber));
+	}
+
 	return newnode;
 }
 
@@ -1280,6 +1289,29 @@ _copyAppendPath(AppendPath *from)
 	return newnode;
 }
 
+/* ----------------
+ *				_copyResultPath
+ * ----------------
+ */
+static ResultPath *
+_copyResultPath(ResultPath *from)
+{
+	ResultPath    *newnode = makeNode(ResultPath);
+
+	/*
+	 * copy the node superclass fields
+	 */
+	CopyPathFields((Path *) from, (Path *) newnode);
+
+	/*
+	 * copy remainder of node
+	 */
+	Node_Copy(from, newnode, subpath);
+	Node_Copy(from, newnode, constantqual);
+
+	return newnode;
+}
+
 /* ----------------
  *		CopyJoinPathFields
  *
@@ -2878,6 +2910,9 @@ copyObject(void *from)
 		case T_AppendPath:
 			retval = _copyAppendPath(from);
 			break;
+		case T_ResultPath:
+			retval = _copyResultPath(from);
+			break;
 		case T_NestPath:
 			retval = _copyNestPath(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 551c32d5dbaec8194a9febb777dee83c97e7a3a6..122de38fed91539647194b6dd0344dd075cb7b97 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -20,7 +20,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.161 2002/10/14 22:14:34 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.162 2002/11/06 00:00:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -463,6 +463,18 @@ _equalAppendPath(AppendPath *a, AppendPath *b)
 	return true;
 }
 
+static bool
+_equalResultPath(ResultPath *a, ResultPath *b)
+{
+	if (!_equalPath((Path *) a, (Path *) b))
+		return false;
+	if (!equal(a->subpath, b->subpath))
+		return false;
+	if (!equal(a->constantqual, b->constantqual))
+		return false;
+	return true;
+}
+
 static bool
 _equalJoinPath(JoinPath *a, JoinPath *b)
 {
@@ -2103,6 +2115,9 @@ equal(void *a, void *b)
 		case T_AppendPath:
 			retval = _equalAppendPath(a, b);
 			break;
+		case T_ResultPath:
+			retval = _equalResultPath(a, b);
+			break;
 		case T_IndexOptInfo:
 			retval = _equalIndexOptInfo(a, b);
 			break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index e1a34118a62fbce7790e374913ff26d00d73370e..2d6db222b29f7fbed753907d71d080738b504e0f 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -5,7 +5,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.176 2002/10/14 22:14:34 tgl Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.177 2002/11/06 00:00:44 tgl Exp $
  *
  * NOTES
  *	  Every (plan) node in POSTGRES has an associated "out" routine which
@@ -597,6 +597,8 @@ _outAgg(StringInfo str, Agg *node)
 {
 	appendStringInfo(str, " AGG ");
 	_outPlanInfo(str, (Plan *) node);
+	appendStringInfo(str, " :aggstrategy %d :numCols %d ",
+					 (int) node->aggstrategy, node->numCols);
 }
 
 static void
@@ -604,11 +606,7 @@ _outGroup(StringInfo str, Group *node)
 {
 	appendStringInfo(str, " GRP ");
 	_outPlanInfo(str, (Plan *) node);
-
-	/* the actual Group fields */
-	appendStringInfo(str, " :numCols %d :tuplePerGroup %s ",
-					 node->numCols,
-					 booltostr(node->tuplePerGroup));
+	appendStringInfo(str, " :numCols %d ", node->numCols);
 }
 
 static void
@@ -1114,6 +1112,26 @@ _outAppendPath(StringInfo str, AppendPath *node)
 	_outNode(str, node->subpaths);
 }
 
+/*
+ *	ResultPath is a subclass of Path.
+ */
+static void
+_outResultPath(StringInfo str, ResultPath *node)
+{
+	appendStringInfo(str,
+					 " RESULTPATH :pathtype %d :startup_cost %.2f :total_cost %.2f :pathkeys ",
+					 node->path.pathtype,
+					 node->path.startup_cost,
+					 node->path.total_cost);
+	_outNode(str, node->path.pathkeys);
+
+	appendStringInfo(str, " :subpath ");
+	_outNode(str, node->subpath);
+
+	appendStringInfo(str, " :constantqual ");
+	_outNode(str, node->constantqual);
+}
+
 /*
  *	NestPath is a subclass of Path
  */
@@ -1717,6 +1735,9 @@ _outNode(StringInfo str, void *obj)
 			case T_AppendPath:
 				_outAppendPath(str, obj);
 				break;
+			case T_ResultPath:
+				_outResultPath(str, obj);
+				break;
 			case T_NestPath:
 				_outNestPath(str, obj);
 				break;
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 33e28413439d32d684a0d5fe46e7fca0f5847cd0..568bf8ee1e4fbea1831b7d37dea48037798b7d2f 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.135 2002/10/14 22:14:34 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.136 2002/11/06 00:00:44 tgl Exp $
  *
  * NOTES
  *	  Most of the read functions for plan nodes are tested. (In fact, they
@@ -696,17 +696,6 @@ _readSort(void)
 	return local_node;
 }
 
-static Agg *
-_readAgg(void)
-{
-	Agg		   *local_node;
-
-	local_node = makeNode(Agg);
-	_getPlan((Plan *) local_node);
-
-	return local_node;
-}
-
 /* ----------------
  *		_readHash
  *
@@ -1880,6 +1869,45 @@ _readAppendPath(void)
 	return local_node;
 }
 
+/* ----------------
+ *		_readResultPath
+ *
+ *	ResultPath is a subclass of Path.
+ * ----------------
+ */
+static ResultPath *
+_readResultPath(void)
+{
+	ResultPath *local_node;
+	char	   *token;
+	int			length;
+
+	local_node = makeNode(ResultPath);
+
+	token = pg_strtok(&length); /* get :pathtype */
+	token = pg_strtok(&length); /* now read it */
+	local_node->path.pathtype = atoi(token);
+
+	token = pg_strtok(&length); /* get :startup_cost */
+	token = pg_strtok(&length); /* now read it */
+	local_node->path.startup_cost = (Cost) atof(token);
+
+	token = pg_strtok(&length); /* get :total_cost */
+	token = pg_strtok(&length); /* now read it */
+	local_node->path.total_cost = (Cost) atof(token);
+
+	token = pg_strtok(&length); /* get :pathkeys */
+	local_node->path.pathkeys = nodeRead(true); /* now read it */
+
+	token = pg_strtok(&length); /* get :subpath */
+	local_node->subpath = nodeRead(true);		/* now read it */
+
+	token = pg_strtok(&length); /* get :constantqual */
+	local_node->constantqual = nodeRead(true);		/* now read it */
+
+	return local_node;
+}
+
 /* ----------------
  *		_readNestPath
  *
@@ -2196,8 +2224,6 @@ parsePlanString(void)
 		return_value = _readFromExpr();
 	else if (length == 8 && strncmp(token, "JOINEXPR", length) == 0)
 		return_value = _readJoinExpr();
-	else if (length == 3 && strncmp(token, "AGG", length) == 0)
-		return_value = _readAgg();
 	else if (length == 4 && strncmp(token, "HASH", length) == 0)
 		return_value = _readHash();
 	else if (length == 6 && strncmp(token, "RESDOM", length) == 0)
@@ -2240,6 +2266,8 @@ parsePlanString(void)
 		return_value = _readTidPath();
 	else if (length == 10 && strncmp(token, "APPENDPATH", length) == 0)
 		return_value = _readAppendPath();
+	else if (length == 10 && strncmp(token, "RESULTPATH", length) == 0)
+		return_value = _readResultPath();
 	else if (length == 8 && strncmp(token, "NESTPATH", length) == 0)
 		return_value = _readNestPath();
 	else if (length == 9 && strncmp(token, "MERGEPATH", length) == 0)
diff --git a/src/backend/optimizer/README b/src/backend/optimizer/README
index 26f23168861b32c563698e2b3bbea02b7ade5e76..698b831a9cf1a48e73b48b7c4ac80a18076c0bf7 100644
--- a/src/backend/optimizer/README
+++ b/src/backend/optimizer/README
@@ -219,11 +219,9 @@ planner()
    pull out constant quals, which can be used to gate execution of the
 	whole plan (if any are found, we make a top-level Result node
 	to do the gating)
-   make a simplified target list that only contains Vars, no expressions
----subplanner()
-    make list of base relations used in query
-    split up the qual into restrictions (a=1) and joins (b=c)
-    find qual clauses that enable merge and hash joins
+   make list of base relations used in query
+   split up the qual into restrictions (a=1) and joins (b=c)
+   find qual clauses that enable merge and hash joins
 ----make_one_rel()
      set_base_rel_pathlist()
       find scan and all index paths for each base relation
@@ -239,7 +237,7 @@ planner()
       cheapest path for each newly constructed joinrel.
       Loop back if this wasn't the top join level.
    Back at query_planner:
-   put back constant quals and non-simplified target list
+    put back any constant quals by adding a Result node
  Back at grouping_planner:
  do grouping(GROUP)
  do aggregates
@@ -257,8 +255,11 @@ RelOptInfo      - a relation or joined relations
  JoinInfo       - join clauses, including the relids needed for the join
 
  Path           - every way to generate a RelOptInfo(sequential,index,joins)
-  SeqScan       - a plain Path node with nodeTag = T_SeqScan
+  SeqScan       - a plain Path node with pathtype = T_SeqScan
   IndexPath     - index scans
+  TidPath       - scan by CTID
+  AppendPath    - append multiple subpaths together
+  ResultPath    - a Result plan (used for variable-free tlist or qual)
   NestPath      - nested-loop joins
   MergePath     - merge joins
   HashPath      - hash joins
@@ -276,10 +277,10 @@ generated during the optimization process are marked with their sort order
 
 It is also possible to avoid an explicit sort step to implement a user's
 ORDER BY clause if the final path has the right ordering already, so the
-sort ordering is of interest even at the top level.  subplanner() will
+sort ordering is of interest even at the top level.  query_planner() will
 look for the cheapest path with a sort order matching the desired order,
-and will compare its cost to the cost of using the cheapest-overall path
-and doing an explicit sort.
+and grouping_planner() will compare its cost to the cost of using the
+cheapest-overall path and doing an explicit sort.
 
 When we are generating paths for a particular RelOptInfo, we discard a path
 if it is more expensive than another known path that has the same or better
diff --git a/src/backend/optimizer/geqo/geqo_misc.c b/src/backend/optimizer/geqo/geqo_misc.c
index ef7b489f5912f36e723b440f577f66e47ccc0afa..7cda46946bc9b4628bc632f73c71255011855772 100644
--- a/src/backend/optimizer/geqo/geqo_misc.c
+++ b/src/backend/optimizer/geqo/geqo_misc.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: geqo_misc.c,v 1.34 2002/09/04 20:31:20 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/geqo/geqo_misc.c,v 1.35 2002/11/06 00:00:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,19 +19,17 @@
    =*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
  */
 
-
-
 #include "postgres.h"
 
 #include "optimizer/geqo_misc.h"
 #include "nodes/print.h"
 
+
 #ifdef GEQO_DEBUG
 
-static float avg_pool(Pool *pool);
 
-/* avg_pool
- *
+/*
+ * avg_pool
  */
 static float
 avg_pool(Pool *pool)
@@ -81,7 +79,6 @@ print_pool(FILE *fp, Pool *pool, int start, int stop)
 /* print_gen
  *
  *	 printout for chromosome: best, worst, mean, average
- *
  */
 void
 print_gen(FILE *fp, Pool *pool, int generation)
@@ -121,133 +118,4 @@ print_edge_table(FILE *fp, Edge *edge_table, int num_gene)
 	fprintf(fp, "\n");
 }
 
-/*************************************************************
- Debug output subroutines
- *************************************************************/
-
-void
-geqo_print_joinclauses(Query *root, List *clauses)
-{
-	List	   *l;
-
-	foreach(l, clauses)
-	{
-		RestrictInfo *c = lfirst(l);
-
-		print_expr((Node *) c->clause, root->rtable);
-		if (lnext(l))
-			printf(" ");
-	}
-}
-
-void
-geqo_print_path(Query *root, Path *path, int indent)
-{
-	char	   *ptype = NULL;
-	JoinPath   *jp;
-	bool		join = false;
-	int			i;
-
-	for (i = 0; i < indent; i++)
-		printf("\t");
-
-	switch (nodeTag(path))
-	{
-		case T_Path:
-			ptype = "SeqScan";
-			join = false;
-			break;
-		case T_IndexPath:
-			ptype = "IdxScan";
-			join = false;
-			break;
-		case T_NestPath:
-			ptype = "Nestloop";
-			join = true;
-			break;
-		case T_MergePath:
-			ptype = "MergeJoin";
-			join = true;
-			break;
-		case T_HashPath:
-			ptype = "HashJoin";
-			join = true;
-			break;
-		default:
-			break;
-	}
-	if (join)
-	{
-		jp = (JoinPath *) path;
-		printf("%s rows=%.0f cost=%.2f..%.2f\n",
-			   ptype, path->parent->rows,
-			   path->startup_cost, path->total_cost);
-		switch (nodeTag(path))
-		{
-			case T_MergePath:
-			case T_HashPath:
-				for (i = 0; i < indent + 1; i++)
-					printf("\t");
-				printf("   clauses=(");
-				geqo_print_joinclauses(root, jp->joinrestrictinfo);
-				printf(")\n");
-
-				if (nodeTag(path) == T_MergePath)
-				{
-					MergePath  *mp = (MergePath *) path;
-
-					if (mp->outersortkeys || mp->innersortkeys)
-					{
-						for (i = 0; i < indent + 1; i++)
-							printf("\t");
-						printf("   sortouter=%d sortinner=%d\n",
-							   ((mp->outersortkeys) ? 1 : 0),
-							   ((mp->innersortkeys) ? 1 : 0));
-					}
-				}
-				break;
-			default:
-				break;
-		}
-		geqo_print_path(root, jp->outerjoinpath, indent + 1);
-		geqo_print_path(root, jp->innerjoinpath, indent + 1);
-	}
-	else
-	{
-		int			relid = lfirsti(path->parent->relids);
-
-		printf("%s(%d) rows=%.0f cost=%.2f..%.2f\n",
-			   ptype, relid, path->parent->rows,
-			   path->startup_cost, path->total_cost);
-
-		if (IsA(path, IndexPath))
-		{
-			printf("  pathkeys=");
-			print_pathkeys(path->pathkeys, root->rtable);
-		}
-	}
-}
-
-void
-geqo_print_rel(Query *root, RelOptInfo *rel)
-{
-	List	   *l;
-
-	printf("______________________________\n");
-	printf("(");
-	foreach(l, rel->relids)
-		printf("%d ", lfirsti(l));
-	printf("): rows=%.0f width=%d\n", rel->rows, rel->width);
-
-	printf("\tpath list:\n");
-	foreach(l, rel->pathlist)
-		geqo_print_path(root, lfirst(l), 1);
-
-	printf("\n\tcheapest startup path:\n");
-	geqo_print_path(root, rel->cheapest_startup_path, 1);
-
-	printf("\n\tcheapest total path:\n");
-	geqo_print_path(root, rel->cheapest_total_path, 1);
-}
-
 #endif   /* GEQO_DEBUG */
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c
index 7d8d6a6bebac44b94e6616700354a9412bf33e8e..ea016e8a2e905df5da087a3aa355cf878a6fa8d8 100644
--- a/src/backend/optimizer/path/allpaths.c
+++ b/src/backend/optimizer/path/allpaths.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.88 2002/09/04 20:31:20 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.89 2002/11/06 00:00:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -742,6 +742,14 @@ print_path(Query *root, Path *path, int indent)
 			ptype = "TidScan";
 			join = false;
 			break;
+		case T_AppendPath:
+			ptype = "Append";
+			join = false;
+			break;
+		case T_ResultPath:
+			ptype = "Result";
+			join = false;
+			break;
 		case T_NestPath:
 			ptype = "Nestloop";
 			join = true;
@@ -762,10 +770,15 @@ print_path(Query *root, Path *path, int indent)
 
 	for (i = 0; i < indent; i++)
 		printf("\t");
-	printf("%s(", ptype);
-	print_relids(path->parent->relids);
-	printf(") rows=%.0f cost=%.2f..%.2f\n",
-		   path->parent->rows, path->startup_cost, path->total_cost);
+	printf("%s", ptype);
+
+	if (path->parent)
+	{
+		printf("(");
+		print_relids(path->parent->relids);
+		printf(") rows=%.0f", path->parent->rows);
+	}
+	printf(" cost=%.2f..%.2f\n", path->startup_cost, path->total_cost);
 
 	if (path->pathkeys)
 	{
@@ -785,7 +798,7 @@ print_path(Query *root, Path *path, int indent)
 		print_restrictclauses(root, jp->joinrestrictinfo);
 		printf("\n");
 
-		if (nodeTag(path) == T_MergePath)
+		if (IsA(path, MergePath))
 		{
 			MergePath  *mp = (MergePath *) path;
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 9cdbcc2e5e548206392263f54ef63d5a87341902..5a2acbd2763b7f2210130adbf942e96a764d4465 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.119 2002/09/18 21:35:21 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.120 2002/11/06 00:00:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,6 +34,7 @@
 static Scan *create_scan_plan(Query *root, Path *best_path);
 static Join *create_join_plan(Query *root, JoinPath *best_path);
 static Append *create_append_plan(Query *root, AppendPath *best_path);
+static Result *create_result_plan(Query *root, ResultPath *best_path);
 static SeqScan *create_seqscan_plan(Path *best_path, List *tlist,
 					List *scan_clauses);
 static IndexScan *create_indexscan_plan(Query *root, IndexPath *best_path,
@@ -135,6 +136,10 @@ create_plan(Query *root, Path *best_path)
 			plan = (Plan *) create_append_plan(root,
 											   (AppendPath *) best_path);
 			break;
+		case T_Result:
+			plan = (Plan *) create_result_plan(root,
+											   (ResultPath *) best_path);
+			break;
 		default:
 			elog(ERROR, "create_plan: unknown pathtype %d",
 				 best_path->pathtype);
@@ -342,6 +347,35 @@ create_append_plan(Query *root, AppendPath *best_path)
 	return plan;
 }
 
+/*
+ * create_result_plan
+ *	  Create a Result plan for 'best_path' and (recursively) plans
+ *	  for its subpaths.
+ *
+ *	  Returns a Plan node.
+ */
+static Result *
+create_result_plan(Query *root, ResultPath *best_path)
+{
+	Result	   *plan;
+	List	   *tlist;
+	Plan	   *subplan;
+
+	if (best_path->path.parent)
+		tlist = best_path->path.parent->targetlist;
+	else
+		tlist = NIL;			/* will be filled in later */
+
+	if (best_path->subpath)
+		subplan = create_plan(root, best_path->subpath);
+	else
+		subplan = NULL;
+
+	plan = make_result(tlist, (Node *) best_path->constantqual, subplan);
+
+	return plan;
+}
+
 
 /*****************************************************************************
  *
@@ -1605,11 +1639,16 @@ make_material(List *tlist, Plan *lefttree)
 }
 
 Agg *
-make_agg(List *tlist, List *qual, Plan *lefttree)
+make_agg(List *tlist, List *qual, AggStrategy aggstrategy,
+		 int ngrp, AttrNumber *grpColIdx, Plan *lefttree)
 {
 	Agg		   *node = makeNode(Agg);
 	Plan	   *plan = &node->plan;
 
+	node->aggstrategy = aggstrategy;
+	node->numCols = ngrp;
+	node->grpColIdx = grpColIdx;
+
 	copy_plan_costsize(plan, lefttree);
 
 	/*
@@ -1621,22 +1660,21 @@ make_agg(List *tlist, List *qual, Plan *lefttree)
 		 length(pull_agg_clause((Node *) qual)));
 
 	/*
-	 * We will produce a single output tuple if the input is not a Group,
+	 * We will produce a single output tuple if not grouping,
 	 * and a tuple per group otherwise.  For now, estimate the number of
 	 * groups as 10% of the number of tuples --- bogus, but how to do
-	 * better? (Note we assume the input Group node is in "tuplePerGroup"
-	 * mode, so it didn't reduce its row count already.)
+	 * better?
 	 */
-	if (IsA(lefttree, Group))
+	if (aggstrategy == AGG_PLAIN)
 	{
-		plan->plan_rows *= 0.1;
-		if (plan->plan_rows < 1)
-			plan->plan_rows = 1;
+		plan->plan_rows = 1;
+		plan->startup_cost = plan->total_cost;
 	}
 	else
 	{
-		plan->plan_rows = 1;
-		plan->startup_cost = plan->total_cost;
+		plan->plan_rows *= 0.1;
+		if (plan->plan_rows < 1)
+			plan->plan_rows = 1;
 	}
 
 	plan->state = (EState *) NULL;
@@ -1650,7 +1688,6 @@ make_agg(List *tlist, List *qual, Plan *lefttree)
 
 Group *
 make_group(List *tlist,
-		   bool tuplePerGroup,
 		   int ngrp,
 		   AttrNumber *grpColIdx,
 		   Plan *lefttree)
@@ -1667,25 +1704,18 @@ make_group(List *tlist,
 	plan->total_cost += cpu_operator_cost * plan->plan_rows * ngrp;
 
 	/*
-	 * If tuplePerGroup (which is named exactly backwards) is true, we
-	 * will return all the input tuples, so the input node's row count is
-	 * OK.	Otherwise, we'll return only one tuple from each group. For
-	 * now, estimate the number of groups as 10% of the number of tuples
+	 * Estimate the number of groups as 10% of the number of tuples
 	 * --- bogus, but how to do better?
 	 */
-	if (!tuplePerGroup)
-	{
-		plan->plan_rows *= 0.1;
-		if (plan->plan_rows < 1)
-			plan->plan_rows = 1;
-	}
+	plan->plan_rows *= 0.1;
+	if (plan->plan_rows < 1)
+		plan->plan_rows = 1;
 
 	plan->state = (EState *) NULL;
 	plan->qual = NULL;
 	plan->targetlist = tlist;
 	plan->lefttree = lefttree;
 	plan->righttree = (Plan *) NULL;
-	node->tuplePerGroup = tuplePerGroup;
 	node->numCols = ngrp;
 	node->grpColIdx = grpColIdx;
 
@@ -1883,9 +1913,6 @@ make_result(List *tlist,
 	Result	   *node = makeNode(Result);
 	Plan	   *plan = &node->plan;
 
-#ifdef NOT_USED
-	tlist = generate_fjoin(tlist);
-#endif
 	if (subplan)
 		copy_plan_costsize(plan, subplan);
 	else
@@ -1906,57 +1933,3 @@ make_result(List *tlist,
 
 	return node;
 }
-
-#ifdef NOT_USED
-List *
-generate_fjoin(List *tlist)
-{
-	List		tlistP;
-	List		newTlist = NIL;
-	List		fjoinList = NIL;
-	int			nIters = 0;
-
-	/*
-	 * Break the target list into elements with Iter nodes, and those
-	 * without them.
-	 */
-	foreach(tlistP, tlist)
-	{
-		List		tlistElem;
-
-		tlistElem = lfirst(tlistP);
-		if (IsA(lsecond(tlistElem), Iter))
-		{
-			nIters++;
-			fjoinList = lappend(fjoinList, tlistElem);
-		}
-		else
-			newTlist = lappend(newTlist, tlistElem);
-	}
-
-	/*
-	 * if we have an Iter node then we need to flatten.
-	 */
-	if (nIters > 0)
-	{
-		List	   *inner;
-		List	   *tempList;
-		Fjoin	   *fjoinNode;
-		DatumPtr	results = (DatumPtr) palloc(nIters * sizeof(Datum));
-		BoolPtr		alwaysDone = (BoolPtr) palloc(nIters * sizeof(bool));
-
-		inner = lfirst(fjoinList);
-		fjoinList = lnext(fjoinList);
-		fjoinNode = (Fjoin) MakeFjoin(false,
-									  nIters,
-									  inner,
-									  results,
-									  alwaysDone);
-		tempList = lcons(fjoinNode, fjoinList);
-		newTlist = lappend(newTlist, tempList);
-	}
-	return newTlist;
-	return tlist;				/* do nothing for now - ay 10/94 */
-}
-
-#endif
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 41f914b8f91e0f25d7c55f585251bef192be835f..4354a5eb035f723b8b01067cc4e47531fa1fae5d 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -14,15 +14,13 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.70 2002/09/02 02:47:02 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.71 2002/11/06 00:00:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
 
-
 #include "optimizer/clauses.h"
-#include "optimizer/cost.h"
 #include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
@@ -31,31 +29,37 @@
 #include "utils/memutils.h"
 
 
-static Plan *subplanner(Query *root, List *flat_tlist,
-		   double tuple_fraction);
-
-
 /*--------------------
  * query_planner
- *	  Generate a plan for a basic query, which may involve joins but
- *	  not any fancier features.
+ *	  Generate a path (that is, a simplified plan) for a basic query,
+ *	  which may involve joins but not any fancier features.
  *
+ * Since query_planner does not handle the toplevel processing (grouping,
+ * sorting, etc) it cannot select the best path by itself.  It selects
+ * two paths: the cheapest path that produces the required tuples, independent
+ * of any ordering considerations, and the cheapest path that produces the
+ * required tuples in the required ordering, if there is a path that
+ * can produce them without an explicit top-level sort step.  The caller
+ * (grouping_planner) will make the final decision about which to use.
+ *
+ * Input parameters:
+ * root is the query to plan
  * tlist is the target list the query should produce (NOT root->targetList!)
  * tuple_fraction is the fraction of tuples we expect will be retrieved
  *
- * Note: the Query node now also includes a query_pathkeys field, which
- * is both an input and an output of query_planner().  The input value
- * signals query_planner that the indicated sort order is wanted in the
- * final output plan.  The output value is the actual pathkeys of the
- * selected path.  This might not be the same as what the caller requested;
- * the caller must do pathkeys_contained_in() to decide whether an
- * explicit sort is still needed.  (The main 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.)  The pathkeys value passed to query_planner
- * has not yet been "canonicalized", since the necessary info does not get
- * computed until subplanner() scans the qual clauses.	We canonicalize it
- * inside subplanner() as soon as that task is done.  The output value
- * will be in canonical form as well.
+ * Output parameters:
+ * *cheapest_path receives the overall-cheapest path for the query
+ * *sorted_path receives the cheapest presorted path for the query,
+ *				if any (it may be NULL, or the same as cheapest_path)
+ *
+ * Note: the Query node also includes a query_pathkeys field, which is both
+ * an input and an output of query_planner().  The input value signals
+ * query_planner that the indicated sort order is wanted in the final output
+ * plan.  But this value has not yet been "canonicalized", since the needed
+ * info does not get computed until we scan the qual clauses.  We canonicalize
+ * it as soon as that task is done.  (The main 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.)
  *
  * tuple_fraction is interpreted as follows:
  *	  0 (or less): expect all tuples to be retrieved (normal case)
@@ -66,18 +70,14 @@ static Plan *subplanner(Query *root, List *flat_tlist,
  * Note that while this routine and its subroutines treat a negative
  * tuple_fraction the same as 0, grouping_planner has a different
  * interpretation.
- *
- * Returns a query plan.
  *--------------------
  */
-Plan *
-query_planner(Query *root,
-			  List *tlist,
-			  double tuple_fraction)
+void
+query_planner(Query *root, List *tlist, double tuple_fraction,
+			  Path **cheapest_path, Path **sorted_path)
 {
 	List	   *constant_quals;
-	List	   *var_only_tlist;
-	Plan	   *subplan;
+	RelOptInfo *final_rel;
 
 	/*
 	 * If the query has an empty join tree, then it's something easy like
@@ -85,11 +85,10 @@ query_planner(Query *root,
 	 */
 	if (root->jointree->fromlist == NIL)
 	{
-		root->query_pathkeys = NIL;		/* signal unordered result */
-
-		/* Make childless Result node to evaluate given tlist. */
-		return (Plan *) make_result(tlist, root->jointree->quals,
-									(Plan *) NULL);
+		*cheapest_path = (Path *) create_result_path(NULL, NULL,
+											(List *) root->jointree->quals);
+		*sorted_path = NULL;
+		return;
 	}
 
 	/*
@@ -107,80 +106,8 @@ query_planner(Query *root,
 							  &constant_quals);
 
 	/*
-	 * Create a target list that consists solely of (resdom var) target
-	 * list entries, i.e., contains no arbitrary expressions.
-	 *
-	 * All subplan nodes will have "flat" (var-only) tlists.
-	 *
-	 * This implies that all expression evaluations are done at the root of
-	 * the plan tree.  Once upon a time there was code to try to push
-	 * expensive function calls down to lower plan nodes, but that's dead
-	 * code and has been for a long time...
-	 */
-	var_only_tlist = flatten_tlist(tlist);
-
-	/*
-	 * Choose the best access path and build a plan for it.
+	 * init planner lists to empty
 	 */
-	subplan = subplanner(root, var_only_tlist, tuple_fraction);
-
-	/*
-	 * Build a result node to control the plan if we have constant quals,
-	 * or if the top-level plan node is one that cannot do expression
-	 * evaluation (it won't be able to evaluate the requested tlist).
-	 * Currently, the only plan node we might see here that falls into
-	 * that category is Append.
-	 *
-	 * XXX future improvement: if the given tlist is flat anyway, we don't
-	 * really need a Result node.
-	 */
-	if (constant_quals || IsA(subplan, Append))
-	{
-		/*
-		 * The result node will also be responsible for evaluating the
-		 * originally requested tlist.
-		 */
-		subplan = (Plan *) make_result(tlist,
-									   (Node *) constant_quals,
-									   subplan);
-	}
-	else
-	{
-		/*
-		 * Replace the toplevel plan node's flattened target list with the
-		 * targetlist given by my caller, so that expressions are
-		 * evaluated.
-		 */
-		subplan->targetlist = tlist;
-	}
-
-	return subplan;
-}
-
-/*
- * subplanner
- *
- *	 Subplanner creates an entire plan consisting of joins and scans
- *	 for processing a single level of attributes.
- *
- * flat_tlist is the flattened target list
- * tuple_fraction is the fraction of tuples we expect will be retrieved
- *
- * See query_planner() comments about the interpretation of tuple_fraction.
- *
- * Returns a subplan.
- */
-static Plan *
-subplanner(Query *root,
-		   List *flat_tlist,
-		   double tuple_fraction)
-{
-	RelOptInfo *final_rel;
-	Plan	   *resultplan;
-	Path	   *cheapestpath;
-	Path	   *presortedpath;
-
-	/* init lists to empty */
 	root->base_rel_list = NIL;
 	root->other_rel_list = NIL;
 	root->join_rel_list = NIL;
@@ -197,8 +124,14 @@ subplanner(Query *root,
 	 * clauses are added to appropriate lists belonging to the mentioned
 	 * relations.  We also build lists of equijoined keys for pathkey
 	 * construction.
+	 *
+	 * Note: all subplan nodes will have "flat" (var-only) tlists.
+	 * This implies that all expression evaluations are done at the root of
+	 * the plan tree.  Once upon a time there was code to try to push
+	 * expensive function calls down to lower plan nodes, but that's dead
+	 * code and has been for a long time...
 	 */
-	build_base_rel_tlists(root, flat_tlist);
+	build_base_rel_tlists(root, tlist);
 
 	(void) distribute_quals_to_rels(root, (Node *) root->jointree);
 
@@ -220,31 +153,8 @@ subplanner(Query *root,
 	 */
 	final_rel = make_one_rel(root);
 
-	if (!final_rel)
-		elog(ERROR, "subplanner: failed to construct a relation");
-
-#ifdef NOT_USED					/* fix xfunc */
-
-	/*
-	 * Perform Predicate Migration on each path, to optimize and correctly
-	 * assess the cost of each before choosing the cheapest one. -- JMH,
-	 * 11/16/92
-	 *
-	 * Needn't do so if the top rel is pruneable: that means there's no
-	 * expensive functions left to pull up.  -- JMH, 11/22/92
-	 */
-	if (XfuncMode != XFUNC_OFF && XfuncMode != XFUNC_NOPM &&
-		XfuncMode != XFUNC_NOPULL && !final_rel->pruneable)
-	{
-		List	   *pathnode;
-
-		foreach(pathnode, final_rel->pathlist)
-		{
-			if (xfunc_do_predmig((Path *) lfirst(pathnode)))
-				set_cheapest(final_rel);
-		}
-	}
-#endif
+	if (!final_rel || !final_rel->cheapest_total_path)
+		elog(ERROR, "query_planner: failed to construct a relation");
 
 	/*
 	 * Now that we have an estimate of the final rel's size, we can
@@ -255,75 +165,35 @@ subplanner(Query *root,
 		tuple_fraction /= final_rel->rows;
 
 	/*
-	 * Determine the cheapest path, independently of any ordering
-	 * considerations.	We do, however, take into account whether the
-	 * whole plan is expected to be evaluated or not.
+	 * Pick out the cheapest-total path and the cheapest presorted path
+	 * for the requested pathkeys (if there is one).  We can take the
+	 * tuple fraction into account when selecting the cheapest presorted
+	 * path, but not when selecting the cheapest-total path, since if we
+	 * have to sort then we'll have to fetch all the tuples.  (But there's
+	 * a special case: if query_pathkeys is NIL, meaning order doesn't
+	 * matter, then the "cheapest presorted" path will be the cheapest
+	 * overall for the tuple fraction.)
 	 */
-	if (tuple_fraction <= 0.0 || tuple_fraction >= 1.0)
-		cheapestpath = final_rel->cheapest_total_path;
-	else
-		cheapestpath =
-			get_cheapest_fractional_path_for_pathkeys(final_rel->pathlist,
-													  NIL,
-													  tuple_fraction);
+	*cheapest_path = final_rel->cheapest_total_path;
 
-	Assert(cheapestpath != NULL);
-
-	/*
-	 * Select the best path and create a subplan to execute it.
-	 *
-	 * If no special sort order is wanted, or if the cheapest path is already
-	 * appropriately ordered, we use the cheapest path found above.
-	 */
-	if (root->query_pathkeys == NIL ||
-		pathkeys_contained_in(root->query_pathkeys,
-							  cheapestpath->pathkeys))
-	{
-		root->query_pathkeys = cheapestpath->pathkeys;
-		resultplan = create_plan(root, cheapestpath);
-		goto plan_built;
-	}
-
-	/*
-	 * Otherwise, look to see if we have an already-ordered path that is
-	 * cheaper than doing an explicit sort on the cheapest-total-cost
-	 * path.
-	 */
-	cheapestpath = final_rel->cheapest_total_path;
-	presortedpath =
+	*sorted_path =
 		get_cheapest_fractional_path_for_pathkeys(final_rel->pathlist,
 												  root->query_pathkeys,
 												  tuple_fraction);
-	if (presortedpath)
-	{
-		Path		sort_path;	/* dummy for result of cost_sort */
-
-		cost_sort(&sort_path, root, root->query_pathkeys,
-				  final_rel->rows, final_rel->width);
-		sort_path.startup_cost += cheapestpath->total_cost;
-		sort_path.total_cost += cheapestpath->total_cost;
-		if (compare_fractional_path_costs(presortedpath, &sort_path,
-										  tuple_fraction) <= 0)
-		{
-			/* Presorted path is cheaper, use it */
-			root->query_pathkeys = presortedpath->pathkeys;
-			resultplan = create_plan(root, presortedpath);
-			goto plan_built;
-		}
-		/* otherwise, doing it the hard way is still cheaper */
-	}
 
 	/*
-	 * Nothing for it but to sort the cheapest-total-cost path --- but we
-	 * let the caller do that.	grouping_planner has to be able to add a
-	 * sort node anyway, so no need for extra code here.  (Furthermore,
-	 * the given pathkeys might involve something we can't compute here,
-	 * such as an aggregate function...)
+	 * If we have constant quals, add a toplevel Result step to process them.
 	 */
-	root->query_pathkeys = cheapestpath->pathkeys;
-	resultplan = create_plan(root, cheapestpath);
-
-plan_built:
-
-	return resultplan;
+	if (constant_quals)
+	{
+		*cheapest_path = (Path *)
+			create_result_path((*cheapest_path)->parent,
+							   *cheapest_path,
+							   constant_quals);
+		if (*sorted_path)
+			*sorted_path = (Path *)
+				create_result_path((*sorted_path)->parent,
+								   *sorted_path,
+								   constant_quals);
+	}
 }
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index b607173a4c32f201b3ba6ab771b1c93c8d23b0d5..cc8e7a698d54b7716f46465a8791a1f7f071c29c 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.125 2002/09/24 18:38:23 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.126 2002/11/06 00:00:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,6 +21,8 @@
 #include "nodes/print.h"
 #endif
 #include "optimizer/clauses.h"
+#include "optimizer/cost.h"
+#include "optimizer/pathnode.h"
 #include "optimizer/paths.h"
 #include "optimizer/planmain.h"
 #include "optimizer/planner.h"
@@ -53,10 +55,10 @@ static Plan *inheritance_planner(Query *parse, List *inheritlist);
 static Plan *grouping_planner(Query *parse, double tuple_fraction);
 static List *make_subplanTargetList(Query *parse, List *tlist,
 					   AttrNumber **groupColIdx);
-static Plan *make_groupplan(Query *parse,
-			   List *group_tlist, bool tuplePerGroup,
-			   List *groupClause, AttrNumber *grpColIdx,
-			   bool is_presorted, Plan *subplan);
+static Plan *make_groupsortplan(Query *parse,
+								List *groupClause,
+								AttrNumber *grpColIdx,
+								Plan *subplan);
 static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);
 
 
@@ -877,9 +879,7 @@ grouping_planner(Query *parse, double tuple_fraction)
 	List	   *tlist = parse->targetList;
 	Plan	   *result_plan;
 	List	   *current_pathkeys;
-	List	   *group_pathkeys;
 	List	   *sort_pathkeys;
-	AttrNumber *groupColIdx = NULL;
 
 	if (parse->setOperations)
 	{
@@ -917,17 +917,20 @@ grouping_planner(Query *parse, double tuple_fraction)
 		current_pathkeys = NIL;
 
 		/*
-		 * Calculate pathkeys that represent grouping/ordering
-		 * requirements (grouping should always be null, but...)
+		 * Calculate pathkeys that represent ordering requirements
 		 */
-		group_pathkeys = make_pathkeys_for_sortclauses(parse->groupClause,
-													   tlist);
 		sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause,
 													  tlist);
+		sort_pathkeys = canonicalize_pathkeys(parse, sort_pathkeys);
 	}
 	else
 	{
+		/* No set operations, do regular planning */
 		List	   *sub_tlist;
+		List	   *group_pathkeys;
+		AttrNumber *groupColIdx = NULL;
+		Path	   *cheapest_path;
+		Path	   *sorted_path;
 
 		/* Preprocess targetlist in case we are inside an INSERT/UPDATE. */
 		tlist = preprocess_targetlist(tlist,
@@ -1192,99 +1195,162 @@ grouping_planner(Query *parse, double tuple_fraction)
 				tuple_fraction = 0.25;
 		}
 
-		/* Generate the basic plan for this Query */
-		result_plan = query_planner(parse,
-									sub_tlist,
-									tuple_fraction);
-
 		/*
-		 * query_planner returns actual sort order (which is not
-		 * necessarily what we requested) in query_pathkeys.
+		 * Generate the best unsorted and presorted paths for this Query
+		 * (but note there may not be any presorted path).
 		 */
-		current_pathkeys = parse->query_pathkeys;
-	}
-
-	/*
-	 * We couldn't canonicalize group_pathkeys and sort_pathkeys before
-	 * running query_planner(), so do it now.
-	 */
-	group_pathkeys = canonicalize_pathkeys(parse, group_pathkeys);
-	sort_pathkeys = canonicalize_pathkeys(parse, sort_pathkeys);
-
-	/*
-	 * If we have a GROUP BY clause, insert a group node (plus the
-	 * appropriate sort node, if necessary).
-	 */
-	if (parse->groupClause)
-	{
-		bool		tuplePerGroup;
-		List	   *group_tlist;
-		bool		is_sorted;
+		query_planner(parse, sub_tlist, tuple_fraction,
+					  &cheapest_path, &sorted_path);
 
 		/*
-		 * Decide whether how many tuples per group the Group node needs
-		 * to return. (Needs only one tuple per group if no aggregate is
-		 * present. Otherwise, need every tuple from the group to do the
-		 * aggregation.)  Note tuplePerGroup is named backwards :-(
+		 * We couldn't canonicalize group_pathkeys and sort_pathkeys before
+		 * running query_planner(), so do it now.
 		 */
-		tuplePerGroup = parse->hasAggs;
+		group_pathkeys = canonicalize_pathkeys(parse, group_pathkeys);
+		sort_pathkeys = canonicalize_pathkeys(parse, sort_pathkeys);
 
 		/*
-		 * If there are aggregates then the Group node should just return
-		 * the same set of vars as the subplan did.  If there are no aggs
-		 * then the Group node had better compute the final tlist.
+		 * Select the best path and create a plan to execute it.
+		 *
+		 * If no special sort order is wanted, or if the cheapest path is
+		 * already appropriately ordered, use the cheapest path.
+		 * Otherwise, look to see if we have an already-ordered path that is
+		 * cheaper than doing an explicit sort on the cheapest-total-cost
+		 * path.
 		 */
-		if (parse->hasAggs)
-			group_tlist = new_unsorted_tlist(result_plan->targetlist);
+		if (parse->query_pathkeys == NIL ||
+			pathkeys_contained_in(parse->query_pathkeys,
+								  cheapest_path->pathkeys))
+		{
+			result_plan = create_plan(parse, cheapest_path);
+			current_pathkeys = cheapest_path->pathkeys;
+		}
+		else if (sorted_path)
+		{
+			Path		sort_path;	/* dummy for result of cost_sort */
+
+			cost_sort(&sort_path, parse, parse->query_pathkeys,
+					  sorted_path->parent->rows, sorted_path->parent->width);
+			sort_path.startup_cost += cheapest_path->total_cost;
+			sort_path.total_cost += cheapest_path->total_cost;
+			if (compare_fractional_path_costs(sorted_path, &sort_path,
+											  tuple_fraction) <= 0)
+			{
+				/* Presorted path is cheaper, use it */
+				result_plan = create_plan(parse, sorted_path);
+				current_pathkeys = sorted_path->pathkeys;
+			}
+			else
+			{
+				/* otherwise, doing it the hard way is still cheaper */
+				result_plan = create_plan(parse, cheapest_path);
+				current_pathkeys = cheapest_path->pathkeys;
+			}
+		}
 		else
-			group_tlist = tlist;
+		{
+			/*
+			 * No sorted path, so we must use the cheapest-total path.
+			 * The actual sort step will be generated below.
+			 */
+			result_plan = create_plan(parse, cheapest_path);
+			current_pathkeys = cheapest_path->pathkeys;
+		}
 
 		/*
-		 * Figure out whether the path result is already ordered the way
-		 * we need it --- if so, no need for an explicit sort step.
+		 * create_plan() returns a plan with just a "flat" tlist of required
+		 * Vars.  We want to insert the sub_tlist as the tlist of the top
+		 * plan node.  If the top-level plan node is one that cannot do
+		 * expression evaluation, we must insert a Result node to project the
+		 * desired tlist.
+		 * Currently, the only plan node we might see here that falls into
+		 * that category is Append.
 		 */
-		if (pathkeys_contained_in(group_pathkeys, current_pathkeys))
+		if (IsA(result_plan, Append))
 		{
-			is_sorted = true;	/* no sort needed now */
-			/* current_pathkeys remains unchanged */
+			result_plan = (Plan *) make_result(sub_tlist, NULL, result_plan);
 		}
 		else
 		{
 			/*
-			 * We will need to do an explicit sort by the GROUP BY clause.
-			 * make_groupplan will do the work, but set current_pathkeys
-			 * to indicate the resulting order.
+			 * Otherwise, just replace the flat tlist with the desired tlist.
 			 */
-			is_sorted = false;
-			current_pathkeys = group_pathkeys;
+			result_plan->targetlist = sub_tlist;
 		}
 
-		result_plan = make_groupplan(parse,
-									 group_tlist,
-									 tuplePerGroup,
-									 parse->groupClause,
-									 groupColIdx,
-									 is_sorted,
-									 result_plan);
-	}
+		/*
+		 * If any aggregate is present, insert the Agg node, plus an explicit
+		 * sort if necessary.
+		 *
+		 * HAVING clause, if any, becomes qual of the Agg node
+		 */
+		if (parse->hasAggs)
+		{
+			AggStrategy aggstrategy;
 
-	/*
-	 * If aggregate is present, insert the Agg node
-	 *
-	 * HAVING clause, if any, becomes qual of the Agg node
-	 */
-	if (parse->hasAggs)
-	{
-		result_plan = (Plan *) make_agg(tlist,
-										(List *) parse->havingQual,
-										result_plan);
-		/* Note: Agg does not affect any existing sort order of the tuples */
-	}
-	else
-	{
-		/* If there are no Aggs, we shouldn't have any HAVING qual anymore */
-		Assert(parse->havingQual == NULL);
-	}
+			if (parse->groupClause)
+			{
+				aggstrategy = AGG_SORTED;
+				/*
+				 * Add an explicit sort if we couldn't make the path come out
+				 * the way the AGG node needs it.
+				 */
+				if (!pathkeys_contained_in(group_pathkeys, current_pathkeys))
+				{
+					result_plan = make_groupsortplan(parse,
+													 parse->groupClause,
+													 groupColIdx,
+													 result_plan);
+					current_pathkeys = group_pathkeys;
+				}
+			}
+			else
+				aggstrategy = AGG_PLAIN;
+
+			result_plan = (Plan *) make_agg(tlist,
+											(List *) parse->havingQual,
+											aggstrategy,
+											length(parse->groupClause),
+											groupColIdx,
+											result_plan);
+			/*
+			 * Note: plain or grouped Agg does not affect any existing
+			 * sort order of the tuples
+			 */
+		}
+		else
+		{
+			/*
+			 * If there are no Aggs, we shouldn't have any HAVING qual anymore
+			 */
+			Assert(parse->havingQual == NULL);
+
+			/*
+			 * If we have a GROUP BY clause, insert a group node (plus the
+			 * appropriate sort node, if necessary).
+			 */
+			if (parse->groupClause)
+			{
+				/*
+				 * Add an explicit sort if we couldn't make the path come out
+				 * the way the GROUP node needs it.
+				 */
+				if (!pathkeys_contained_in(group_pathkeys, current_pathkeys))
+				{
+					result_plan = make_groupsortplan(parse,
+													 parse->groupClause,
+													 groupColIdx,
+													 result_plan);
+					current_pathkeys = group_pathkeys;
+				}
+
+				result_plan = (Plan *) make_group(tlist,
+												  length(parse->groupClause),
+												  groupColIdx,
+												  result_plan);
+			}
+		}
+	} /* end of if (setOperations) */
 
 	/*
 	 * If we were not able to make the plan come out in the right order,
@@ -1323,7 +1389,7 @@ grouping_planner(Query *parse, double tuple_fraction)
  * make_subplanTargetList
  *	  Generate appropriate target list when grouping is required.
  *
- * When grouping_planner inserts Aggregate and/or Group plan nodes above
+ * When grouping_planner inserts Aggregate 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.
@@ -1433,62 +1499,48 @@ make_subplanTargetList(Query *parse,
 }
 
 /*
- * 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.
+ * make_groupsortplan
+ *		Add a Sort node to explicitly sort according to the GROUP BY clause.
+ *
+ * Note: the Sort node always just takes a copy of the subplan's tlist
+ * plus ordering information.  (This might seem inefficient if the
+ * subplan contains complex GROUP BY expressions, but in fact Sort
+ * does not evaluate its targetlist --- it only outputs the same
+ * tuples in a new order.  So the expressions we might be copying
+ * are just dummies with no extra execution cost.)
  */
 static Plan *
-make_groupplan(Query *parse,
-			   List *group_tlist,
-			   bool tuplePerGroup,
-			   List *groupClause,
-			   AttrNumber *grpColIdx,
-			   bool is_presorted,
-			   Plan *subplan)
+make_groupsortplan(Query *parse,
+				   List *groupClause,
+				   AttrNumber *grpColIdx,
+				   Plan *subplan)
 {
-	int			numCols = length(groupClause);
+	List	   *sort_tlist = new_unsorted_tlist(subplan->targetlist);
+	int			keyno = 0;
+	List	   *gl;
 
-	if (!is_presorted)
+	foreach(gl, groupClause)
 	{
+		GroupClause *grpcl = (GroupClause *) lfirst(gl);
+		TargetEntry *te = nth(grpColIdx[keyno] - 1, sort_tlist);
+		Resdom	   *resdom = te->resdom;
+
 		/*
-		 * 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.)
+		 * 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!
 		 */
-		List	   *sort_tlist = new_unsorted_tlist(subplan->targetlist);
-		int			keyno = 0;
-		List	   *gl;
-
-		foreach(gl, groupClause)
+		if (resdom->reskey == 0)
 		{
-			GroupClause *grpcl = (GroupClause *) lfirst(gl);
-			TargetEntry *te = nth(grpColIdx[keyno] - 1, sort_tlist);
-			Resdom	   *resdom = te->resdom;
-
-			/*
-			 * Check for the possibility of duplicate group-by clauses ---
-			 * the parser should have removed 'em, but the Sort executor
-			 * will get terribly confused if any get through!
-			 */
-			if (resdom->reskey == 0)
-			{
-				/* OK, insert the ordering info needed by the executor. */
-				resdom->reskey = ++keyno;
-				resdom->reskeyop = grpcl->sortop;
-			}
+			/* OK, insert the ordering info needed by the executor. */
+			resdom->reskey = ++keyno;
+			resdom->reskeyop = grpcl->sortop;
 		}
-
-		Assert(keyno > 0);
-
-		subplan = (Plan *) make_sort(parse, sort_tlist, subplan, keyno);
 	}
 
-	return (Plan *) make_group(group_tlist, tuplePerGroup, numCols,
-							   grpColIdx, subplan);
+	Assert(keyno > 0);
+
+	return (Plan *) make_sort(parse, sort_tlist, subplan, keyno);
 }
 
 /*
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 4b3c9809b8bae00c7ffad103f3b9185f47cc7e40..7dd0dce68911cd08a903c92450e17af030809f58 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.78 2002/06/20 20:29:31 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.79 2002/11/06 00:00:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -405,7 +405,6 @@ create_tidscan_path(Query *root, RelOptInfo *rel, List *tideval)
  * create_append_path
  *	  Creates a path corresponding to an Append plan, returning the
  *	  pathnode.
- *
  */
 AppendPath *
 create_append_path(RelOptInfo *rel, List *subpaths)
@@ -433,6 +432,41 @@ create_append_path(RelOptInfo *rel, List *subpaths)
 	return pathnode;
 }
 
+/*
+ * create_result_path
+ *	  Creates a path corresponding to a Result plan, returning the
+ *	  pathnode.
+ */
+ResultPath *
+create_result_path(RelOptInfo *rel, Path *subpath, List *constantqual)
+{
+	ResultPath *pathnode = makeNode(ResultPath);
+
+	pathnode->path.pathtype = T_Result;
+	pathnode->path.parent = rel; /* may be NULL */
+
+	if (subpath)
+		pathnode->path.pathkeys = subpath->pathkeys;
+	else
+		pathnode->path.pathkeys = NIL;
+
+	pathnode->subpath = subpath;
+	pathnode->constantqual = constantqual;
+
+	if (subpath)
+	{
+		pathnode->path.startup_cost = subpath->startup_cost;
+		pathnode->path.total_cost = subpath->total_cost;
+	}
+	else
+	{
+		pathnode->path.startup_cost = 0;
+		pathnode->path.total_cost = cpu_tuple_cost;
+	}
+
+	return pathnode;
+}
+
 /*
  * create_subqueryscan_path
  *	  Creates a path corresponding to a sequential scan of a subquery,
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index facef908947b9efa83f18b1af509138dafef21e1..533d296186a8ecd50f9dc716e8a6cabd2d4a8af9 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.75 2002/09/04 20:31:42 momjian Exp $
+ * $Id: execnodes.h,v 1.76 2002/11/06 00:00:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -673,6 +673,8 @@ typedef struct AggState
 	CommonScanState csstate;	/* its first field is NodeTag */
 	List	   *aggs;			/* all Aggref nodes in targetlist & quals */
 	int			numaggs;		/* length of list (could be zero!) */
+	FmgrInfo   *eqfunctions;	/* per-grouping-field equality fns */
+	HeapTuple	grp_firstTuple;	/* copy of first tuple of current group */
 	AggStatePerAgg peragg;		/* per-Aggref working state */
 	MemoryContext tup_cxt;		/* context for per-output-tuple
 								 * expressions */
@@ -691,7 +693,7 @@ typedef struct GroupState
 	FmgrInfo   *eqfunctions;	/* per-field lookup data for equality fns */
 	bool		grp_useFirstTuple;		/* first tuple not processed yet */
 	bool		grp_done;
-	HeapTuple	grp_firstTuple;
+	HeapTuple	grp_firstTuple;	/* copy of first tuple of current group */
 } GroupState;
 
 /* ----------------
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 5320c1b10d2b386bf92ceafe82fdebc8675d890a..aad54a9c26c09c5665e28a533ce9bd7c62fb237d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodes.h,v 1.120 2002/10/11 04:16:44 momjian Exp $
+ * $Id: nodes.h,v 1.121 2002/11/06 00:00:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -82,6 +82,7 @@ typedef enum NodeTag
 	T_HashPath,
 	T_TidPath,
 	T_AppendPath,
+	T_ResultPath,
 	T_PathKeyItem,
 	T_RestrictInfo,
 	T_JoinInfo,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ee0d2210134382f2ecf81034c7ab8db84c98ed9c..8303fe9d09fcb5913d5f3db60bb4a5757bdb769a 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parsenodes.h,v 1.209 2002/10/14 22:14:35 tgl Exp $
+ * $Id: parsenodes.h,v 1.210 2002/11/06 00:00:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -101,7 +101,7 @@ typedef struct Query
 	List	   *join_rel_list;	/* list of join-relation RelOptInfos */
 	List	   *equi_key_list;	/* list of lists of equijoined
 								 * PathKeyItems */
-	List	   *query_pathkeys; /* pathkeys for query_planner()'s result */
+	List	   *query_pathkeys; /* desired pathkeys for query_planner() */
 } Query;
 
 
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 8d26fea6f094c0be05066f5beb9a6c196495d085..63c8f20d807de76728f5ee975131364a86bc5f92 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: plannodes.h,v 1.58 2002/09/04 20:31:44 momjian Exp $
+ * $Id: plannodes.h,v 1.59 2002/11/06 00:00:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -140,17 +140,23 @@ typedef struct Plan
  * ===============
  */
 
-/* all plan nodes "derive" from the Plan structure by having the
-   Plan structure as the first field.  This ensures that everything works
-   when nodes are cast to Plan's.  (node pointers are frequently cast to Plan*
-   when passed around generically in the executor */
+/*
+ * all plan nodes "derive" from the Plan structure by having the
+ * Plan structure as the first field.  This ensures that everything works
+ * when nodes are cast to Plan's.  (node pointers are frequently cast to Plan*
+ * when passed around generically in the executor)
+ */
 
 
 /* ----------------
  *	 Result node -
  *		If no outer plan, evaluate a variable-free targetlist.
- *		If outer plan, return tuples from outer plan that satisfy
- *		given quals (we can also do a level of projection)
+ *		If outer plan, return tuples from outer plan (after a level of
+ *		projection as shown by targetlist).
+ *
+ * If resconstantqual isn't NULL, it represents a one-time qualification
+ * test (i.e., one that doesn't depend on any variables from the outer plan,
+ * so needs to be evaluated only once).
  * ----------------
  */
 typedef struct Result
@@ -318,30 +324,45 @@ typedef struct HashJoin
 
 /* ---------------
  *		aggregate node
+ *
+ * An Agg node implements plain or grouped aggregation.  For grouped
+ * aggregation, we can work with presorted input or unsorted input;
+ * the latter strategy uses an internal hashtable.
+ *
+ * Notice the lack of any direct info about the aggregate functions to be
+ * computed.  They are found by scanning the node's tlist and quals during
+ * executor startup.  (It is possible that there are no aggregate functions;
+ * this could happen if they get optimized away by constant-folding, or if
+ * we are using the Agg node to implement hash-based grouping.)
  * ---------------
  */
+typedef enum AggStrategy
+{
+	AGG_PLAIN,					/* simple agg across all input rows */
+	AGG_SORTED,					/* grouped agg, input must be sorted */
+	AGG_HASHED					/* grouped agg, use internal hashtable */
+} AggStrategy;
+
 typedef struct Agg
 {
 	Plan		plan;
+	AggStrategy	aggstrategy;
+	int			numCols;		/* number of grouping columns */
+	AttrNumber *grpColIdx;		/* their indexes in the target list */
 	AggState   *aggstate;
 } Agg;
 
 /* ---------------
  *	 group node -
- *		use for queries with GROUP BY specified.
- *
- *		If tuplePerGroup is true, one tuple (with group columns only) is
- *		returned for each group and NULL is returned when there are no more
- *		groups. Otherwise, all the tuples of a group are returned with a
- *		NULL returned at the end of each group. (see nodeGroup.c for details)
+ *		Used for queries with GROUP BY (but no aggregates) specified.
+ *		The input must be presorted according to the grouping columns.
  * ---------------
  */
 typedef struct Group
 {
 	Plan		plan;
-	bool		tuplePerGroup;	/* what tuples to return (see above) */
-	int			numCols;		/* number of group columns */
-	AttrNumber *grpColIdx;		/* indexes into the target list */
+	int			numCols;		/* number of grouping columns */
+	AttrNumber *grpColIdx;		/* their indexes in the target list */
 	GroupState *grpstate;
 } Group;
 
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 071addbc1bbec90a0721fb973003fea27ad92898..480fd9630becedbf3368c57ffb636de0a390e883 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -1,13 +1,13 @@
 /*-------------------------------------------------------------------------
  *
  * relation.h
- *	  Definitions for internal planner nodes.
+ *	  Definitions for planner's internal data structures.
  *
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: relation.h,v 1.67 2002/09/04 20:31:44 momjian Exp $
+ * $Id: relation.h,v 1.68 2002/11/06 00:00:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -402,6 +402,19 @@ typedef struct AppendPath
 	List	   *subpaths;		/* list of component Paths */
 } AppendPath;
 
+/*
+ * ResultPath represents use of a Result plan node, either to compute a
+ * variable-free targetlist or to gate execution of a subplan with a
+ * one-time (variable-free) qual condition.  Note that in the former case
+ * path.parent will be NULL; in the latter case it is copied from the subpath.
+ */
+typedef struct ResultPath
+{
+	Path		path;
+	Path	   *subpath;
+	List	   *constantqual;
+} ResultPath;
+
 /*
  * All join-type paths share these fields.
  */
diff --git a/src/include/optimizer/geqo_misc.h b/src/include/optimizer/geqo_misc.h
index 6599bf7697f3181caa8b762f9c94540de4f43f21..5c276c0fb21daffcee813f76b6fff31dde66733f 100644
--- a/src/include/optimizer/geqo_misc.h
+++ b/src/include/optimizer/geqo_misc.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: geqo_misc.h,v 1.21 2002/09/04 20:31:45 momjian Exp $
+ * $Id: geqo_misc.h,v 1.22 2002/11/06 00:00:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -32,9 +32,6 @@ extern void print_pool(FILE *fp, Pool *pool, int start, int stop);
 extern void print_gen(FILE *fp, Pool *pool, int generation);
 extern void print_edge_table(FILE *fp, Edge *edge_table, int num_gene);
 
-extern void geqo_print_rel(Query *root, RelOptInfo *rel);
-extern void geqo_print_path(Query *root, Path *path, int indent);
-extern void geqo_print_joinclauses(Query *root, List *clauses);
 #endif   /* GEQO_DEBUG */
 
 #endif   /* GEQO_MISC_H */
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 0c9ce6c95e38d77844845b65a600c3021214acdf..61c433e12dd34ca9aa11042dedc17046d4a6ac58 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: pathnode.h,v 1.44 2002/06/20 20:29:51 momjian Exp $
+ * $Id: pathnode.h,v 1.45 2002/11/06 00:00:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,6 +35,8 @@ extern IndexPath *create_index_path(Query *root, RelOptInfo *rel,
 extern TidPath *create_tidscan_path(Query *root, RelOptInfo *rel,
 					List *tideval);
 extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths);
+extern ResultPath *create_result_path(RelOptInfo *rel, Path *subpath,
+									  List *constantqual);
 extern Path *create_subqueryscan_path(RelOptInfo *rel);
 extern Path *create_functionscan_path(Query *root, RelOptInfo *rel);
 
diff --git a/src/include/optimizer/planmain.h b/src/include/optimizer/planmain.h
index d31cee5ddc797c0f9960454b608306ccf2c57959..c927d54074038e6a1ed9e361b782debc5fa1dbb2 100644
--- a/src/include/optimizer/planmain.h
+++ b/src/include/optimizer/planmain.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: planmain.h,v 1.60 2002/09/04 20:31:45 momjian Exp $
+ * $Id: planmain.h,v 1.61 2002/11/06 00:00:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,8 @@
 /*
  * prototypes for plan/planmain.c
  */
-extern Plan *query_planner(Query *root, List *tlist, double tuple_fraction);
+extern void query_planner(Query *root, List *tlist, double tuple_fraction,
+						  Path **cheapest_path, Path **sorted_path);
 
 /*
  * prototypes for plan/createplan.c
@@ -33,9 +34,10 @@ extern Sort *make_sort(Query *root, List *tlist,
 		  Plan *lefttree, int keycount);
 extern Sort *make_sort_from_pathkeys(Query *root, List *tlist,
 						Plan *lefttree, List *pathkeys);
-extern Agg *make_agg(List *tlist, List *qual, Plan *lefttree);
-extern Group *make_group(List *tlist, bool tuplePerGroup, int ngrp,
-		   AttrNumber *grpColIdx, Plan *lefttree);
+extern Agg *make_agg(List *tlist, List *qual, AggStrategy aggstrategy,
+					 int ngrp, AttrNumber *grpColIdx, Plan *lefttree);
+extern Group *make_group(List *tlist, int ngrp, AttrNumber *grpColIdx,
+						 Plan *lefttree);
 extern Material *make_material(List *tlist, Plan *lefttree);
 extern Unique *make_unique(List *tlist, Plan *lefttree, List *distinctList);
 extern Limit *make_limit(List *tlist, Plan *lefttree,