From 1fd0c59e25063e664f8a5cee6f723470c5979544 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 5 Dec 2002 15:50:39 +0000
Subject: [PATCH] Phase 1 of read-only-plans project: cause executor state
 nodes to point to plan nodes, not vice-versa.  All executor state nodes now
 inherit from struct PlanState.  Copying of plan trees has been simplified by
 not storing a list of SubPlans in Plan nodes (eliminating duplicate links).
 The executor still needs such a list, but it can build it during
 ExecutorStart since it has to scan the plan tree anyway. No initdb forced
 since no stored-on-disk structures changed, but you will need a full
 recompile because of node-numbering changes.

---
 src/backend/commands/explain.c          | 189 ++++++----
 src/backend/commands/portalcmds.c       |  14 +-
 src/backend/commands/prepare.c          |  25 +-
 src/backend/executor/README             |  23 +-
 src/backend/executor/execAmi.c          | 157 ++++----
 src/backend/executor/execMain.c         | 263 ++++++-------
 src/backend/executor/execProcnode.c     | 450 ++++++++++++-----------
 src/backend/executor/execQual.c         | 168 ++++++++-
 src/backend/executor/execScan.c         |  24 +-
 src/backend/executor/execTuples.c       |  10 +-
 src/backend/executor/execUtils.c        |  79 ++--
 src/backend/executor/functions.c        | 147 ++++----
 src/backend/executor/nodeAgg.c          | 270 +++++++-------
 src/backend/executor/nodeAppend.c       | 126 +++----
 src/backend/executor/nodeFunctionscan.c | 140 +++----
 src/backend/executor/nodeGroup.c        | 129 +++----
 src/backend/executor/nodeHash.c         |  83 ++---
 src/backend/executor/nodeHashjoin.c     | 227 ++++++------
 src/backend/executor/nodeIndexscan.c    | 232 ++++++------
 src/backend/executor/nodeLimit.c        | 164 ++++-----
 src/backend/executor/nodeMaterial.c     | 104 +++---
 src/backend/executor/nodeMergejoin.c    | 383 ++++++++++---------
 src/backend/executor/nodeNestloop.c     | 153 ++++----
 src/backend/executor/nodeResult.c       | 122 +++----
 src/backend/executor/nodeSeqscan.c      | 147 ++++----
 src/backend/executor/nodeSetOp.c        | 150 ++++----
 src/backend/executor/nodeSort.c         | 126 +++----
 src/backend/executor/nodeSubplan.c      | 131 ++++---
 src/backend/executor/nodeSubqueryscan.c | 109 +++---
 src/backend/executor/nodeTidscan.c      | 189 ++++------
 src/backend/executor/nodeUnique.c       | 102 +++---
 src/backend/executor/spi.c              | 110 +++---
 src/backend/nodes/copyfuncs.c           | 466 ++----------------------
 src/backend/nodes/equalfuncs.c          | 209 +----------
 src/backend/nodes/outfuncs.c            |  14 +-
 src/backend/optimizer/plan/createplan.c |  33 +-
 src/backend/optimizer/plan/setrefs.c    |  67 ++--
 src/backend/optimizer/plan/subselect.c  |  36 +-
 src/backend/optimizer/util/pathnode.c   |   6 +-
 src/backend/tcop/pquery.c               |  98 ++---
 src/backend/utils/mmgr/portalmem.c      |  15 +-
 src/include/executor/execdesc.h         |  22 +-
 src/include/executor/executor.h         |  61 ++--
 src/include/executor/nodeAgg.h          |  12 +-
 src/include/executor/nodeAppend.h       |  12 +-
 src/include/executor/nodeFunctionscan.h |  16 +-
 src/include/executor/nodeGroup.h        |  12 +-
 src/include/executor/nodeHash.h         |  13 +-
 src/include/executor/nodeHashjoin.h     |  13 +-
 src/include/executor/nodeIndexscan.h    |  19 +-
 src/include/executor/nodeLimit.h        |  12 +-
 src/include/executor/nodeMaterial.h     |  16 +-
 src/include/executor/nodeMergejoin.h    |  14 +-
 src/include/executor/nodeNestloop.h     |  13 +-
 src/include/executor/nodeResult.h       |  12 +-
 src/include/executor/nodeSeqscan.h      |  16 +-
 src/include/executor/nodeSetOp.h        |  12 +-
 src/include/executor/nodeSort.h         |  16 +-
 src/include/executor/nodeSubplan.h      |  20 +-
 src/include/executor/nodeSubqueryscan.h |  12 +-
 src/include/executor/nodeTidscan.h      |  17 +-
 src/include/executor/nodeUnique.h       |  12 +-
 src/include/nodes/execnodes.h           | 310 ++++++++--------
 src/include/nodes/nodes.h               | 103 +++---
 src/include/nodes/params.h              |   6 +-
 src/include/nodes/plannodes.h           | 233 ++++--------
 src/include/nodes/print.h               |   6 +-
 src/include/nodes/relation.h            |   5 +-
 src/include/tcop/pquery.h               |   4 +-
 src/include/utils/portal.h              |  16 +-
 src/pl/plpgsql/src/pl_exec.c            |   3 +-
 71 files changed, 3001 insertions(+), 3727 deletions(-)

diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index 0e6ca88fd45..e2d1a6eeab1 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.93 2002/11/13 00:39:46 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.94 2002/12/05 15:50:30 tgl Exp $
  *
  */
 
@@ -34,17 +34,19 @@ typedef struct ExplainState
 {
 	/* options */
 	bool		printCost;		/* print cost */
-	bool		printNodes;		/* do nodeToString() instead */
-	bool		printAnalyze;		/* print actual times */
+	bool		printNodes;		/* do nodeToString() too */
+	bool		printAnalyze;	/* print actual times */
 	/* other states */
 	List	   *rtable;			/* range table */
 } ExplainState;
 
-static StringInfo Explain_PlanToString(Plan *plan, ExplainState *es);
 static void ExplainOneQuery(Query *query, ExplainStmt *stmt,
 				TupOutputState *tstate);
-static void explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
-				int indent, ExplainState *es);
+static double elapsed_time(struct timeval *starttime);
+static void explain_outNode(StringInfo str,
+							Plan *plan, PlanState *planstate,
+							Plan *outer_plan,
+							int indent, ExplainState *es);
 static void show_scan_qual(List *qual, bool is_or_qual, const char *qlabel,
 			   int scanrelid, Plan *outer_plan,
 			   StringInfo str, int indent, ExplainState *es);
@@ -116,8 +118,11 @@ static void
 ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
 {
 	Plan	   *plan;
+	QueryDesc  *queryDesc;
 	ExplainState *es;
+	StringInfo	str;
 	double		totaltime = 0;
+	struct timeval starttime;
 
 	/* planner will not cope with utility statements */
 	if (query->commandType == CMD_UTILITY)
@@ -136,41 +141,34 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
 	if (plan == NULL)
 		return;
 
+	/* We don't support DECLARE CURSOR here */
+	Assert(!query->isPortal);
+
+	gettimeofday(&starttime, NULL);
+
+	/* Create a QueryDesc requesting no output */
+	queryDesc = CreateQueryDesc(query, plan, None, NULL, NULL,
+								stmt->analyze);
+
+	/* call ExecutorStart to prepare the plan for execution */
+	ExecutorStart(queryDesc);
+
 	/* Execute the plan for statistics if asked for */
 	if (stmt->analyze)
 	{
-		struct timeval starttime;
-		struct timeval endtime;
-
-		/*
-		 * Set up the instrumentation for the top node. This will cascade
-		 * during plan initialisation
-		 */
-		plan->instrument = InstrAlloc();
+		/* run the plan */
+		ExecutorRun(queryDesc, ForwardScanDirection, 0L);
 
-		gettimeofday(&starttime, NULL);
-		ProcessQuery(query, plan, None, NULL);
-		CommandCounterIncrement();
-		gettimeofday(&endtime, NULL);
+		/* We can't clean up 'till we're done printing the stats... */
 
-		endtime.tv_sec -= starttime.tv_sec;
-		endtime.tv_usec -= starttime.tv_usec;
-		while (endtime.tv_usec < 0)
-		{
-			endtime.tv_usec += 1000000;
-			endtime.tv_sec--;
-		}
-		totaltime = (double) endtime.tv_sec +
-			(double) endtime.tv_usec / 1000000.0;
+		totaltime += elapsed_time(&starttime);
 	}
 
 	es = (ExplainState *) palloc0(sizeof(ExplainState));
 
 	es->printCost = true;		/* default */
-
-	if (stmt->verbose)
-		es->printNodes = true;
-
+	es->printNodes = stmt->verbose;
+	es->printAnalyze = stmt->analyze;
 	es->rtable = query->rtable;
 
 	if (es->printNodes)
@@ -193,33 +191,73 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate)
 		}
 	}
 
+	str = makeStringInfo();
+
 	if (es->printCost)
 	{
-		StringInfo	str;
+		explain_outNode(str, plan, queryDesc->planstate,
+						NULL, 0, es);
+	}
 
-		str = Explain_PlanToString(plan, es);
+	/*
+	 * Close down the query and free resources.  Include time for this
+	 * in the total runtime.
+	 */
+	gettimeofday(&starttime, NULL);
+
+	ExecutorEnd(queryDesc);
+	CommandCounterIncrement();
+
+	totaltime += elapsed_time(&starttime);
+
+	if (es->printCost)
+	{
 		if (stmt->analyze)
 			appendStringInfo(str, "Total runtime: %.2f msec\n",
 							 1000.0 * totaltime);
 		do_text_output_multiline(tstate, str->data);
-		pfree(str->data);
-		pfree(str);
 	}
 
+	pfree(str->data);
+	pfree(str);
 	pfree(es);
 }
 
+/* Compute elapsed time in seconds since given gettimeofday() timestamp */
+static double
+elapsed_time(struct timeval *starttime)
+{
+	struct timeval endtime;
+
+	gettimeofday(&endtime, NULL);
+
+	endtime.tv_sec -= starttime->tv_sec;
+	endtime.tv_usec -= starttime->tv_usec;
+	while (endtime.tv_usec < 0)
+	{
+		endtime.tv_usec += 1000000;
+		endtime.tv_sec--;
+	}
+	return (double) endtime.tv_sec +
+		(double) endtime.tv_usec / 1000000.0;
+}
 
 /*
  * explain_outNode -
  *	  converts a Plan node into ascii string and appends it to 'str'
  *
+ * planstate points to the executor state node corresponding to the plan node.
+ * We need this to get at the instrumentation data (if any) as well as the
+ * list of subplans.
+ *
  * outer_plan, if not null, references another plan node that is the outer
  * side of a join with the current node.  This is only interesting for
  * deciphering runtime keys of an inner indexscan.
  */
 static void
-explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
+explain_outNode(StringInfo str,
+				Plan *plan, PlanState *planstate,
+				Plan *outer_plan,
 				int indent, ExplainState *es)
 {
 	List	   *l;
@@ -410,18 +448,23 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
 						 plan->startup_cost, plan->total_cost,
 						 plan->plan_rows, plan->plan_width);
 
-		if (plan->instrument && plan->instrument->nloops > 0)
+		/*
+		 * We have to forcibly clean up the instrumentation state because
+		 * we haven't done ExecutorEnd yet.  This is pretty grotty ...
+		 */
+		InstrEndLoop(planstate->instrument);
+
+		if (planstate->instrument && planstate->instrument->nloops > 0)
 		{
-			double		nloops = plan->instrument->nloops;
+			double		nloops = planstate->instrument->nloops;
 
 			appendStringInfo(str, " (actual time=%.2f..%.2f rows=%.0f loops=%.0f)",
-							 1000.0 * plan->instrument->startup / nloops,
-							 1000.0 * plan->instrument->total / nloops,
-							 plan->instrument->ntuples / nloops,
-							 plan->instrument->nloops);
-			es->printAnalyze = true;
+							 1000.0 * planstate->instrument->startup / nloops,
+							 1000.0 * planstate->instrument->total / nloops,
+							 planstate->instrument->ntuples / nloops,
+							 planstate->instrument->nloops);
 		}
-		else if( es->printAnalyze )
+		else if (es->printAnalyze)
 		{
 			appendStringInfo(str, " (never executed)");
 		}
@@ -538,6 +581,7 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
 	if (plan->initPlan)
 	{
 		List	   *saved_rtable = es->rtable;
+		List	   *pslist = planstate->initPlan;
 		List	   *lst;
 
 		for (i = 0; i < indent; i++)
@@ -545,12 +589,18 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
 		appendStringInfo(str, "  InitPlan\n");
 		foreach(lst, plan->initPlan)
 		{
-			es->rtable = ((SubPlan *) lfirst(lst))->rtable;
+			SubPlan	   *subplan = (SubPlan *) lfirst(lst);
+			SubPlanState *subplanstate = (SubPlanState *) lfirst(pslist);
+
+			es->rtable = subplan->rtable;
 			for (i = 0; i < indent; i++)
 				appendStringInfo(str, "  ");
 			appendStringInfo(str, "    ->  ");
-			explain_outNode(str, ((SubPlan *) lfirst(lst))->plan, NULL,
+			explain_outNode(str, subplan->plan,
+							subplanstate->planstate,
+							NULL,
 							indent + 4, es);
+			pslist = lnext(pslist);
 		}
 		es->rtable = saved_rtable;
 	}
@@ -561,7 +611,10 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
 		for (i = 0; i < indent; i++)
 			appendStringInfo(str, "  ");
 		appendStringInfo(str, "  ->  ");
-		explain_outNode(str, outerPlan(plan), NULL, indent + 3, es);
+		explain_outNode(str, outerPlan(plan),
+						outerPlanState(planstate),
+						NULL,
+						indent + 3, es);
 	}
 
 	/* righttree */
@@ -570,15 +623,20 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
 		for (i = 0; i < indent; i++)
 			appendStringInfo(str, "  ");
 		appendStringInfo(str, "  ->  ");
-		explain_outNode(str, innerPlan(plan), outerPlan(plan),
+		explain_outNode(str, innerPlan(plan),
+						innerPlanState(planstate),
+						outerPlan(plan),
 						indent + 3, es);
 	}
 
 	if (IsA(plan, Append))
 	{
 		Append	   *appendplan = (Append *) plan;
+		AppendState *appendstate = (AppendState *) planstate;
 		List	   *lst;
+		int			j;
 
+		j = 0;
 		foreach(lst, appendplan->appendplans)
 		{
 			Plan	   *subnode = (Plan *) lfirst(lst);
@@ -587,13 +645,18 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
 				appendStringInfo(str, "  ");
 			appendStringInfo(str, "  ->  ");
 
-			explain_outNode(str, subnode, NULL, indent + 3, es);
+			explain_outNode(str, subnode,
+							appendstate->appendplans[j],
+							NULL,
+							indent + 3, es);
+			j++;
 		}
 	}
 
 	if (IsA(plan, SubqueryScan))
 	{
 		SubqueryScan *subqueryscan = (SubqueryScan *) plan;
+		SubqueryScanState *subquerystate = (SubqueryScanState *) planstate;
 		Plan	   *subnode = subqueryscan->subplan;
 		RangeTblEntry *rte = rt_fetch(subqueryscan->scan.scanrelid,
 									  es->rtable);
@@ -606,13 +669,16 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
 			appendStringInfo(str, "  ");
 		appendStringInfo(str, "  ->  ");
 
-		explain_outNode(str, subnode, NULL, indent + 3, es);
+		explain_outNode(str, subnode,
+						subquerystate->subplan,
+						NULL,
+						indent + 3, es);
 
 		es->rtable = saved_rtable;
 	}
 
 	/* subPlan-s */
-	if (plan->subPlan)
+	if (planstate->subPlan)
 	{
 		List	   *saved_rtable = es->rtable;
 		List	   *lst;
@@ -620,29 +686,24 @@ explain_outNode(StringInfo str, Plan *plan, Plan *outer_plan,
 		for (i = 0; i < indent; i++)
 			appendStringInfo(str, "  ");
 		appendStringInfo(str, "  SubPlan\n");
-		foreach(lst, plan->subPlan)
+		foreach(lst, planstate->subPlan)
 		{
-			es->rtable = ((SubPlan *) lfirst(lst))->rtable;
+			SubPlanState *sps = (SubPlanState *) lfirst(lst);
+			SubPlan *sp = (SubPlan *) sps->ps.plan;
+
+			es->rtable = sp->rtable;
 			for (i = 0; i < indent; i++)
 				appendStringInfo(str, "  ");
 			appendStringInfo(str, "    ->  ");
-			explain_outNode(str, ((SubPlan *) lfirst(lst))->plan, NULL,
+			explain_outNode(str, sp->plan,
+							sps->planstate,
+							NULL,
 							indent + 4, es);
 		}
 		es->rtable = saved_rtable;
 	}
 }
 
-static StringInfo
-Explain_PlanToString(Plan *plan, ExplainState *es)
-{
-	StringInfo	str = makeStringInfo();
-
-	if (plan != NULL)
-		explain_outNode(str, plan, NULL, 0, es);
-	return str;
-}
-
 /*
  * Show a qualifier expression for a scan plan node
  */
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 812cb05dacf..c8607fcf324 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.4 2002/11/13 00:44:08 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.5 2002/12/05 15:50:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,6 +23,10 @@
 
 /*
  * PortalCleanup
+ *
+ * Clean up a portal when it's dropped.  Since this mainly exists to run
+ * ExecutorEnd(), it should not be set as the cleanup hook until we have
+ * called ExecutorStart() on the portal's query.
  */
 void
 PortalCleanup(Portal portal)
@@ -43,7 +47,7 @@ PortalCleanup(Portal portal)
 	/*
 	 * tell the executor to shutdown the query
 	 */
-	ExecutorEnd(PortalGetQueryDesc(portal), PortalGetState(portal));
+	ExecutorEnd(PortalGetQueryDesc(portal));
 
 	/*
 	 * switch back to previous context
@@ -116,7 +120,7 @@ PerformPortalFetch(char *name,
 	oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
 
 	queryDesc = PortalGetQueryDesc(portal);
-	estate = PortalGetState(portal);
+	estate = queryDesc->estate;
 
 	/*
 	 * If the requested destination is not the same as the query's
@@ -158,7 +162,7 @@ PerformPortalFetch(char *name,
 		else
 			direction = ForwardScanDirection;
 
-		ExecutorRun(queryDesc, estate, direction, (long) count);
+		ExecutorRun(queryDesc, direction, (long) count);
 
 		if (estate->es_processed > 0)
 			portal->atStart = false;	/* OK to back up now */
@@ -172,7 +176,7 @@ PerformPortalFetch(char *name,
 		else
 			direction = BackwardScanDirection;
 
-		ExecutorRun(queryDesc, estate, direction, (long) count);
+		ExecutorRun(queryDesc, direction, (long) count);
 
 		if (estate->es_processed > 0)
 			portal->atEnd = false;		/* OK to go forward now */
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 391ef0bc9c5..9a570c8f681 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -6,7 +6,7 @@
  * Copyright (c) 2002, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.8 2002/11/15 00:47:22 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.9 2002/12/05 15:50:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -49,7 +49,7 @@ static void InitQueryHashTable(void);
 static void StoreQuery(const char *stmt_name, List *query_list,
 		   List *plan_list, List *argtype_list);
 static QueryHashEntry *FetchQuery(const char *plan_name);
-static void RunQuery(QueryDesc *qdesc, EState *state);
+static void RunQuery(QueryDesc *qdesc);
 
 
 /*
@@ -151,15 +151,12 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
 		else
 		{
 			QueryDesc  *qdesc;
-			EState	   *state;
 
 			if (log_executor_stats)
 				ResetUsage();
 
-			qdesc = CreateQueryDesc(query, plan, outputDest, NULL);
-			state = CreateExecutorState();
-
-			state->es_param_list_info = paramLI;
+			qdesc = CreateQueryDesc(query, plan, outputDest, NULL,
+									paramLI, false);
 
 			if (stmt->into)
 			{
@@ -170,7 +167,7 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
 				qdesc->dest = None;
 			}
 
-			RunQuery(qdesc, state);
+			RunQuery(qdesc);
 
 			if (log_executor_stats)
 				ShowUsage("EXECUTOR STATISTICS");
@@ -334,15 +331,11 @@ FetchQueryParams(const char *plan_name)
  * Actually execute a prepared query.
  */
 static void
-RunQuery(QueryDesc *qdesc, EState *state)
+RunQuery(QueryDesc *qdesc)
 {
-	TupleDesc	tupdesc;
-
-	tupdesc = ExecutorStart(qdesc, state);
-
-	ExecutorRun(qdesc, state, state->es_direction, 0L);
-
-	ExecutorEnd(qdesc, state);
+	ExecutorStart(qdesc);
+	ExecutorRun(qdesc, ForwardScanDirection, 0L);
+	ExecutorEnd(qdesc);
 }
 
 /*
diff --git a/src/backend/executor/README b/src/backend/executor/README
index 0a56c3fa6ae..d9b0ea1275a 100644
--- a/src/backend/executor/README
+++ b/src/backend/executor/README
@@ -1,4 +1,4 @@
-$Header: /cvsroot/pgsql/src/backend/executor/README,v 1.1 2001/05/15 00:35:50 tgl Exp $
+$Header: /cvsroot/pgsql/src/backend/executor/README,v 1.2 2002/12/05 15:50:30 tgl Exp $
 
 The Postgres Executor
 ---------------------
@@ -39,6 +39,27 @@ delivered by the plan tree.
 XXX a great deal more documentation needs to be written here...
 
 
+Plan Trees and State Trees
+--------------------------
+
+The plan tree delivered by the planner contains a tree of Plan nodes (struct
+types derived from struct Plan).  Each Plan node may have expression trees
+associated with it, to represent its target list, qualification conditions,
+etc.  During executor startup we build a parallel tree of identical structure
+containing executor state nodes --- every plan and expression node type has
+a corresponding executor state node type.  Each node in the state tree has a
+pointer to its corresponding node in the plan tree, plus executor state data
+as needed to implement that node type.  This arrangement allows the plan
+tree to be completely read-only as far as the executor is concerned: all data
+that is modified during execution is in the state tree.  Read-only plan trees
+make life much simpler for plan caching and reuse.
+
+Altogether there are four classes of nodes used in these trees: Plan nodes,
+their corresponding PlanState nodes, Expr nodes, and their corresponding
+ExprState nodes.  (Actually, there are also List nodes, which are used as
+"glue" in all four kinds of tree.)
+
+
 EvalPlanQual (READ COMMITTED update checking)
 ---------------------------------------------
 
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index 1d7bf67f60b..4ac4781ec7c 100644
--- a/src/backend/executor/execAmi.c
+++ b/src/backend/executor/execAmi.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: execAmi.c,v 1.65 2002/11/30 05:21:01 tgl Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/executor/execAmi.c,v 1.66 2002/12/05 15:50:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -19,12 +19,12 @@
 #include "executor/instrument.h"
 #include "executor/nodeAgg.h"
 #include "executor/nodeAppend.h"
+#include "executor/nodeFunctionscan.h"
 #include "executor/nodeGroup.h"
 #include "executor/nodeGroup.h"
 #include "executor/nodeHash.h"
 #include "executor/nodeHashjoin.h"
 #include "executor/nodeIndexscan.h"
-#include "executor/nodeTidscan.h"
 #include "executor/nodeLimit.h"
 #include "executor/nodeMaterial.h"
 #include "executor/nodeMergejoin.h"
@@ -35,45 +35,45 @@
 #include "executor/nodeSort.h"
 #include "executor/nodeSubplan.h"
 #include "executor/nodeSubqueryscan.h"
-#include "executor/nodeFunctionscan.h"
+#include "executor/nodeTidscan.h"
 #include "executor/nodeUnique.h"
 
 
 /* ----------------------------------------------------------------
  *		ExecReScan
  *
- *		XXX this should be extended to cope with all the node types..
- *
  *		takes the new expression context as an argument, so that
  *		index scans needn't have their scan keys updated separately
  *		- marcel 09/20/94
  * ----------------------------------------------------------------
  */
 void
-ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
+ExecReScan(PlanState *node, ExprContext *exprCtxt)
 {
+	/* If collecting timing stats, update them */
 	if (node->instrument)
 		InstrEndLoop(node->instrument);
 
-	if (node->chgParam != NULL) /* Wow! */
+	/* If we have changed parameters, propagate that info */
+	if (node->chgParam != NIL)
 	{
 		List	   *lst;
 
 		foreach(lst, node->initPlan)
 		{
-			Plan	   *splan = ((SubPlan *) lfirst(lst))->plan;
+			PlanState  *splan = ((SubPlanState *) lfirst(lst))->planstate;
 
-			if (splan->extParam != NULL)		/* don't care about child
+			if (splan->plan->extParam != NIL)	/* don't care about child
 												 * locParam */
 				SetChangedParamList(splan, node->chgParam);
-			if (splan->chgParam != NULL)
-				ExecReScanSetParamPlan((SubPlan *) lfirst(lst), node);
+			if (splan->chgParam != NIL)
+				ExecReScanSetParamPlan((SubPlanState *) lfirst(lst), node);
 		}
 		foreach(lst, node->subPlan)
 		{
-			Plan	   *splan = ((SubPlan *) lfirst(lst))->plan;
+			PlanState  *splan = ((SubPlanState *) lfirst(lst))->planstate;
 
-			if (splan->extParam != NULL)
+			if (splan->plan->extParam != NIL)
 				SetChangedParamList(splan, node->chgParam);
 		}
 		/* Well. Now set chgParam for left/right trees. */
@@ -85,76 +85,76 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
 
 	switch (nodeTag(node))
 	{
-		case T_SeqScan:
-			ExecSeqReScan((SeqScan *) node, exprCtxt, parent);
+		case T_ResultState:
+			ExecReScanResult((ResultState *) node, exprCtxt);
 			break;
 
-		case T_IndexScan:
-			ExecIndexReScan((IndexScan *) node, exprCtxt, parent);
+		case T_AppendState:
+			ExecReScanAppend((AppendState *) node, exprCtxt);
 			break;
 
-		case T_TidScan:
-			ExecTidReScan((TidScan *) node, exprCtxt, parent);
+		case T_SeqScanState:
+			ExecSeqReScan((SeqScanState *) node, exprCtxt);
 			break;
 
-		case T_SubqueryScan:
-			ExecSubqueryReScan((SubqueryScan *) node, exprCtxt, parent);
+		case T_IndexScanState:
+			ExecIndexReScan((IndexScanState *) node, exprCtxt);
 			break;
 
-		case T_FunctionScan:
-			ExecFunctionReScan((FunctionScan *) node, exprCtxt, parent);
+		case T_TidScanState:
+			ExecTidReScan((TidScanState *) node, exprCtxt);
 			break;
 
-		case T_Material:
-			ExecMaterialReScan((Material *) node, exprCtxt, parent);
+		case T_SubqueryScanState:
+			ExecSubqueryReScan((SubqueryScanState *) node, exprCtxt);
 			break;
 
-		case T_NestLoop:
-			ExecReScanNestLoop((NestLoop *) node, exprCtxt, parent);
+		case T_FunctionScanState:
+			ExecFunctionReScan((FunctionScanState *) node, exprCtxt);
 			break;
 
-		case T_HashJoin:
-			ExecReScanHashJoin((HashJoin *) node, exprCtxt, parent);
+		case T_NestLoopState:
+			ExecReScanNestLoop((NestLoopState *) node, exprCtxt);
 			break;
 
-		case T_Hash:
-			ExecReScanHash((Hash *) node, exprCtxt, parent);
+		case T_MergeJoinState:
+			ExecReScanMergeJoin((MergeJoinState *) node, exprCtxt);
 			break;
 
-		case T_Agg:
-			ExecReScanAgg((Agg *) node, exprCtxt, parent);
+		case T_HashJoinState:
+			ExecReScanHashJoin((HashJoinState *) node, exprCtxt);
 			break;
 
-		case T_Group:
-			ExecReScanGroup((Group *) node, exprCtxt, parent);
+		case T_MaterialState:
+			ExecMaterialReScan((MaterialState *) node, exprCtxt);
 			break;
 
-		case T_Result:
-			ExecReScanResult((Result *) node, exprCtxt, parent);
+		case T_SortState:
+			ExecReScanSort((SortState *) node, exprCtxt);
 			break;
 
-		case T_Unique:
-			ExecReScanUnique((Unique *) node, exprCtxt, parent);
+		case T_GroupState:
+			ExecReScanGroup((GroupState *) node, exprCtxt);
 			break;
 
-		case T_SetOp:
-			ExecReScanSetOp((SetOp *) node, exprCtxt, parent);
+		case T_AggState:
+			ExecReScanAgg((AggState *) node, exprCtxt);
 			break;
 
-		case T_Limit:
-			ExecReScanLimit((Limit *) node, exprCtxt, parent);
+		case T_UniqueState:
+			ExecReScanUnique((UniqueState *) node, exprCtxt);
 			break;
 
-		case T_Sort:
-			ExecReScanSort((Sort *) node, exprCtxt, parent);
+		case T_HashState:
+			ExecReScanHash((HashState *) node, exprCtxt);
 			break;
 
-		case T_MergeJoin:
-			ExecReScanMergeJoin((MergeJoin *) node, exprCtxt, parent);
+		case T_SetOpState:
+			ExecReScanSetOp((SetOpState *) node, exprCtxt);
 			break;
 
-		case T_Append:
-			ExecReScanAppend((Append *) node, exprCtxt, parent);
+		case T_LimitState:
+			ExecReScanLimit((LimitState *) node, exprCtxt);
 			break;
 
 		default:
@@ -163,10 +163,10 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
 			return;
 	}
 
-	if (node->chgParam != NULL)
+	if (node->chgParam != NIL)
 	{
 		freeList(node->chgParam);
-		node->chgParam = NULL;
+		node->chgParam = NIL;
 	}
 }
 
@@ -176,37 +176,37 @@ ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent)
  * Marks the current scan position.
  */
 void
-ExecMarkPos(Plan *node)
+ExecMarkPos(PlanState *node)
 {
 	switch (nodeTag(node))
 	{
-		case T_SeqScan:
-			ExecSeqMarkPos((SeqScan *) node);
+		case T_SeqScanState:
+			ExecSeqMarkPos((SeqScanState *) node);
 			break;
 
-		case T_IndexScan:
-			ExecIndexMarkPos((IndexScan *) node);
+		case T_IndexScanState:
+			ExecIndexMarkPos((IndexScanState *) node);
 			break;
 
-		case T_TidScan:
-			ExecTidMarkPos((TidScan *) node);
+		case T_TidScanState:
+			ExecTidMarkPos((TidScanState *) node);
 			break;
 
-		case T_FunctionScan:
-			ExecFunctionMarkPos((FunctionScan *) node);
+		case T_FunctionScanState:
+			ExecFunctionMarkPos((FunctionScanState *) node);
 			break;
 
-		case T_Material:
-			ExecMaterialMarkPos((Material *) node);
+		case T_MaterialState:
+			ExecMaterialMarkPos((MaterialState *) node);
 			break;
 
-		case T_Sort:
-			ExecSortMarkPos((Sort *) node);
+		case T_SortState:
+			ExecSortMarkPos((SortState *) node);
 			break;
 
 		default:
 			/* don't make hard error unless caller asks to restore... */
-			elog(LOG, "ExecMarkPos: node type %d not supported",
+			elog(DEBUG1, "ExecMarkPos: node type %d not supported",
 				 nodeTag(node));
 			break;
 	}
@@ -218,32 +218,32 @@ ExecMarkPos(Plan *node)
  * restores the scan position previously saved with ExecMarkPos()
  */
 void
-ExecRestrPos(Plan *node)
+ExecRestrPos(PlanState *node)
 {
 	switch (nodeTag(node))
 	{
-		case T_SeqScan:
-			ExecSeqRestrPos((SeqScan *) node);
+		case T_SeqScanState:
+			ExecSeqRestrPos((SeqScanState *) node);
 			break;
 
-		case T_IndexScan:
-			ExecIndexRestrPos((IndexScan *) node);
+		case T_IndexScanState:
+			ExecIndexRestrPos((IndexScanState *) node);
 			break;
 
-		case T_TidScan:
-			ExecTidRestrPos((TidScan *) node);
+		case T_TidScanState:
+			ExecTidRestrPos((TidScanState *) node);
 			break;
 
-		case T_FunctionScan:
-			ExecFunctionRestrPos((FunctionScan *) node);
+		case T_FunctionScanState:
+			ExecFunctionRestrPos((FunctionScanState *) node);
 			break;
 
-		case T_Material:
-			ExecMaterialRestrPos((Material *) node);
+		case T_MaterialState:
+			ExecMaterialRestrPos((MaterialState *) node);
 			break;
 
-		case T_Sort:
-			ExecSortRestrPos((Sort *) node);
+		case T_SortState:
+			ExecSortRestrPos((SortState *) node);
 			break;
 
 		default:
@@ -258,6 +258,7 @@ ExecRestrPos(Plan *node)
  *
  * XXX Ideally, all plan node types would support mark/restore, and this
  * wouldn't be needed.  For now, this had better match the routines above.
+ * But note the test is on Plan nodetype, not PlanState nodetype.
  */
 bool
 ExecSupportsMarkRestore(NodeTag plantype)
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 92026666894..15d47df669f 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -12,10 +12,9 @@
  *	ExecutorRun() and ExecutorEnd()
  *
  *	These three procedures are the external interfaces to the executor.
- *	In each case, the query descriptor and the execution state is required
- *	as arguments
+ *	In each case, the query descriptor is required as an argument.
  *
- *	ExecutorStart() must be called at the beginning of any execution of any
+ *	ExecutorStart() must be called at the beginning of execution of any
  *	query plan and ExecutorEnd() should always be called at the end of
  *	execution of a plan.
  *
@@ -27,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.189 2002/12/05 04:04:42 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.190 2002/12/05 15:50:30 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -48,16 +47,13 @@
 
 
 /* decls for local routines only used within this module */
-static TupleDesc InitPlan(CmdType operation,
-		 Query *parseTree,
-		 Plan *plan,
-		 EState *estate);
+static void InitPlan(QueryDesc *queryDesc);
 static void initResultRelInfo(ResultRelInfo *resultRelInfo,
 				  Index resultRelationIndex,
 				  List *rangeTable,
 				  CmdType operation);
-static void EndPlan(Plan *plan, EState *estate);
-static TupleTableSlot *ExecutePlan(EState *estate, Plan *plan,
+static void EndPlan(PlanState *planstate, EState *estate);
+static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate,
 			CmdType operation,
 			long numberTuples,
 			ScanDirection direction,
@@ -73,11 +69,6 @@ static void ExecUpdate(TupleTableSlot *slot, ItemPointer tupleid,
 		   EState *estate);
 static TupleTableSlot *EvalPlanQualNext(EState *estate);
 static void EndEvalPlanQual(EState *estate);
-static void ExecCheckQueryPerms(CmdType operation, Query *parseTree,
-					Plan *plan);
-static void ExecCheckPlanPerms(Plan *plan, List *rangeTable,
-				   CmdType operation);
-static void ExecCheckRTPerms(List *rangeTable, CmdType operation);
 static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation);
 
 /* end of local decls */
@@ -89,26 +80,40 @@ static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation);
  *		This routine must be called at the beginning of any execution of any
  *		query plan
  *
- *		returns a TupleDesc which describes the attributes of the tuples to
- *		be returned by the query.  (Same value is saved in queryDesc)
+ * Takes a QueryDesc previously created by CreateQueryDesc (it's not real
+ * clear why we bother to separate the two functions, but...).  The tupDesc
+ * field of the QueryDesc is filled in to describe the tuples that will be
+ * returned, and the internal fields (estate and planstate) are set up.
  *
+ * XXX this will change soon:
  * NB: the CurrentMemoryContext when this is called must be the context
  * to be used as the per-query context for the query plan.	ExecutorRun()
  * and ExecutorEnd() must be called in this same memory context.
  * ----------------------------------------------------------------
  */
-TupleDesc
-ExecutorStart(QueryDesc *queryDesc, EState *estate)
+void
+ExecutorStart(QueryDesc *queryDesc)
 {
-	TupleDesc	result;
+	EState	   *estate;
 
-	/* sanity checks */
+	/* sanity checks: queryDesc must not be started already */
 	Assert(queryDesc != NULL);
+	Assert(queryDesc->estate == NULL);
+
+	/*
+	 * Build EState, fill with parameters from queryDesc
+	 */
+	estate = CreateExecutorState();
+	queryDesc->estate = estate;
+
+	estate->es_param_list_info = queryDesc->params;
 
 	if (queryDesc->plantree->nParamExec > 0)
 		estate->es_param_exec_vals = (ParamExecData *)
 			palloc0(queryDesc->plantree->nParamExec * sizeof(ParamExecData));
 
+	estate->es_instrument = queryDesc->doInstrument;
+
 	/*
 	 * Make our own private copy of the current query snapshot data.
 	 *
@@ -119,16 +124,9 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate)
 	estate->es_snapshot = CopyQuerySnapshot();
 
 	/*
-	 * Initialize the plan
+	 * Initialize the plan state tree
 	 */
-	result = InitPlan(queryDesc->operation,
-					  queryDesc->parsetree,
-					  queryDesc->plantree,
-					  estate);
-
-	queryDesc->tupDesc = result;
-
-	return result;
+	InitPlan(queryDesc);
 }
 
 /* ----------------------------------------------------------------
@@ -150,11 +148,11 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate)
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
-ExecutorRun(QueryDesc *queryDesc, EState *estate,
+ExecutorRun(QueryDesc *queryDesc,
 			ScanDirection direction, long count)
 {
 	CmdType		operation;
-	Plan	   *plan;
+	EState	   *estate;
 	CommandDest dest;
 	DestReceiver *destfunc;
 	TupleTableSlot *result;
@@ -169,7 +167,7 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate,
 	 * feature.
 	 */
 	operation = queryDesc->operation;
-	plan = queryDesc->plantree;
+	estate = queryDesc->estate;
 	dest = queryDesc->dest;
 
 	/*
@@ -189,7 +187,7 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate,
 		result = NULL;
 	else
 		result = ExecutePlan(estate,
-							 plan,
+							 queryDesc->planstate,
 							 operation,
 							 count,
 							 direction,
@@ -211,12 +209,16 @@ ExecutorRun(QueryDesc *queryDesc, EState *estate,
  * ----------------------------------------------------------------
  */
 void
-ExecutorEnd(QueryDesc *queryDesc, EState *estate)
+ExecutorEnd(QueryDesc *queryDesc)
 {
+	EState	   *estate;
+
 	/* sanity checks */
 	Assert(queryDesc != NULL);
 
-	EndPlan(queryDesc->plantree, estate);
+	estate = queryDesc->estate;
+
+	EndPlan(queryDesc->planstate, estate);
 
 	if (estate->es_snapshot != NULL)
 	{
@@ -235,97 +237,55 @@ ExecutorEnd(QueryDesc *queryDesc, EState *estate)
 
 
 /*
- * ExecCheckQueryPerms
- *		Check access permissions for all relations referenced in a query.
+ * CreateExecutorState
  */
-static void
-ExecCheckQueryPerms(CmdType operation, Query *parseTree, Plan *plan)
+EState *
+CreateExecutorState(void)
 {
+	EState	   *state;
+
 	/*
-	 * Check RTEs in the query's primary rangetable.
+	 * create a new executor state
 	 */
-	ExecCheckRTPerms(parseTree->rtable, operation);
+	state = makeNode(EState);
 
 	/*
-	 * Search for subplans and APPEND nodes to check their rangetables.
+	 * initialize the Executor State structure
 	 */
-	ExecCheckPlanPerms(plan, parseTree->rtable, operation);
-}
-
-/*
- * ExecCheckPlanPerms
- *		Recursively scan the plan tree to check access permissions in
- *		subplans.
- */
-static void
-ExecCheckPlanPerms(Plan *plan, List *rangeTable, CmdType operation)
-{
-	List	   *subp;
+	state->es_direction = ForwardScanDirection;
+	state->es_range_table = NIL;
 
-	if (plan == NULL)
-		return;
-
-	/* Check subplans, which we assume are plain SELECT queries */
-
-	foreach(subp, plan->initPlan)
-	{
-		SubPlan    *subplan = (SubPlan *) lfirst(subp);
-
-		ExecCheckRTPerms(subplan->rtable, CMD_SELECT);
-		ExecCheckPlanPerms(subplan->plan, subplan->rtable, CMD_SELECT);
-	}
-	foreach(subp, plan->subPlan)
-	{
-		SubPlan    *subplan = (SubPlan *) lfirst(subp);
+	state->es_result_relations = NULL;
+	state->es_num_result_relations = 0;
+	state->es_result_relation_info = NULL;
 
-		ExecCheckRTPerms(subplan->rtable, CMD_SELECT);
-		ExecCheckPlanPerms(subplan->plan, subplan->rtable, CMD_SELECT);
-	}
+	state->es_junkFilter = NULL;
 
-	/* Check lower plan nodes */
+	state->es_into_relation_descriptor = NULL;
 
-	ExecCheckPlanPerms(plan->lefttree, rangeTable, operation);
-	ExecCheckPlanPerms(plan->righttree, rangeTable, operation);
+	state->es_param_list_info = NULL;
+	state->es_param_exec_vals = NULL;
 
-	/* Do node-type-specific checks */
+	state->es_tupleTable = NULL;
 
-	switch (nodeTag(plan))
-	{
-		case T_SubqueryScan:
-			{
-				SubqueryScan *scan = (SubqueryScan *) plan;
-				RangeTblEntry *rte;
+	state->es_query_cxt = CurrentMemoryContext;
 
-				/* Recursively check the subquery */
-				rte = rt_fetch(scan->scan.scanrelid, rangeTable);
-				Assert(rte->rtekind == RTE_SUBQUERY);
-				ExecCheckQueryPerms(operation, rte->subquery, scan->subplan);
-				break;
-			}
-		case T_Append:
-			{
-				Append	   *app = (Append *) plan;
-				List	   *appendplans;
+	state->es_instrument = false;
 
-				foreach(appendplans, app->appendplans)
-				{
-					ExecCheckPlanPerms((Plan *) lfirst(appendplans),
-									   rangeTable,
-									   operation);
-				}
-				break;
-			}
+	state->es_per_tuple_exprcontext = NULL;
 
-		default:
-			break;
-	}
+	/*
+	 * return the executor state structure
+	 */
+	return state;
 }
 
+
 /*
  * ExecCheckRTPerms
  *		Check access permissions for all relations listed in a range table.
  */
-static void
+void
 ExecCheckRTPerms(List *rangeTable, CmdType operation)
 {
 	List	   *lp;
@@ -350,11 +310,18 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
 	AclResult	aclcheck_result;
 
 	/*
-	 * Only plain-relation RTEs need to be checked here.  Subquery RTEs
-	 * will be checked when ExecCheckPlanPerms finds the SubqueryScan
-	 * node, and function RTEs are checked by init_fcache when the
-	 * function is prepared for execution.	Join and special RTEs need no
-	 * checks.
+	 * If it's a subquery, recursively examine its rangetable.
+	 */
+	if (rte->rtekind == RTE_SUBQUERY)
+	{
+		ExecCheckRTPerms(rte->subquery->rtable, operation);
+		return;
+	}
+
+	/*
+	 * Otherwise, only plain-relation RTEs need to be checked here.
+	 * Function RTEs are checked by init_fcache when the function is prepared
+	 * for execution. Join and special RTEs need no checks.
 	 */
 	if (rte->rtekind != RTE_RELATION)
 		return;
@@ -367,7 +334,7 @@ ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation)
 	 *
 	 * Note: GetUserId() is presently fast enough that there's no harm in
 	 * calling it separately for each RTE.	If that stops being true, we
-	 * could call it once in ExecCheckQueryPerms and pass the userid down
+	 * could call it once in ExecCheckRTPerms and pass the userid down
 	 * from there.	But for now, no need for the extra clutter.
 	 */
 	userid = rte->checkAsUser ? rte->checkAsUser : GetUserId();
@@ -428,7 +395,8 @@ typedef struct execRowMark
 
 typedef struct evalPlanQual
 {
-	Plan	   *plan;
+	Plan	   *plan;			/* XXX temporary */
+	PlanState  *planstate;
 	Index		rti;
 	EState		estate;
 	struct evalPlanQual *free;
@@ -441,17 +409,24 @@ typedef struct evalPlanQual
  *		and start up the rule manager
  * ----------------------------------------------------------------
  */
-static TupleDesc
-InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
+static void
+InitPlan(QueryDesc *queryDesc)
 {
+	CmdType		operation = queryDesc->operation;
+	Query *parseTree = queryDesc->parsetree;
+	Plan *plan = queryDesc->plantree;
+	EState *estate = queryDesc->estate;
+	PlanState  *planstate;
 	List	   *rangeTable;
 	Relation	intoRelationDesc;
 	TupleDesc	tupType;
 
 	/*
-	 * Do permissions checks.
+	 * Do permissions checks.  It's sufficient to examine the query's
+	 * top rangetable here --- subplan RTEs will be checked during
+	 * ExecInitSubPlan().
 	 */
-	ExecCheckQueryPerms(operation, parseTree, plan);
+	ExecCheckRTPerms(parseTree->rtable, operation);
 
 	/*
 	 * get information from query descriptor
@@ -575,14 +550,14 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 	 * query tree.	This opens files, allocates storage and leaves us
 	 * ready to start processing tuples.
 	 */
-	ExecInitNode(plan, estate, NULL);
+	planstate = ExecInitNode(plan, estate);
 
 	/*
 	 * Get the tuple descriptor describing the type of tuples to return.
 	 * (this is especially important if we are creating a relation with
 	 * "SELECT INTO")
 	 */
-	tupType = ExecGetTupType(plan);		/* tuple descriptor */
+	tupType = ExecGetTupType(planstate);
 
 	/*
 	 * Initialize the junk filter if needed. SELECT and INSERT queries
@@ -627,26 +602,29 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 			 */
 			if (parseTree->resultRelations != NIL)
 			{
-				List	   *subplans;
+				PlanState **appendplans;
+				int			as_nplans;
 				ResultRelInfo *resultRelInfo;
+				int			i;
 
 				/* Top plan had better be an Append here. */
 				Assert(IsA(plan, Append));
 				Assert(((Append *) plan)->isTarget);
-				subplans = ((Append *) plan)->appendplans;
-				Assert(length(subplans) == estate->es_num_result_relations);
+				Assert(IsA(planstate, AppendState));
+				appendplans = ((AppendState *) planstate)->appendplans;
+				as_nplans = ((AppendState *) planstate)->as_nplans;
+				Assert(as_nplans == estate->es_num_result_relations);
 				resultRelInfo = estate->es_result_relations;
-				while (subplans != NIL)
+				for (i = 0; i < as_nplans; i++)
 				{
-					Plan	   *subplan = (Plan *) lfirst(subplans);
+					PlanState  *subplan = appendplans[i];
 					JunkFilter *j;
 
-					j = ExecInitJunkFilter(subplan->targetlist,
+					j = ExecInitJunkFilter(subplan->plan->targetlist,
 										   ExecGetTupType(subplan),
 							  ExecAllocTableSlot(estate->es_tupleTable));
 					resultRelInfo->ri_junkFilter = j;
 					resultRelInfo++;
-					subplans = lnext(subplans);
 				}
 
 				/*
@@ -661,7 +639,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 				/* Normal case with just one JunkFilter */
 				JunkFilter *j;
 
-				j = ExecInitJunkFilter(plan->targetlist,
+				j = ExecInitJunkFilter(planstate->plan->targetlist,
 									   tupType,
 							  ExecAllocTableSlot(estate->es_tupleTable));
 				estate->es_junkFilter = j;
@@ -755,7 +733,8 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
 
 	estate->es_into_relation_descriptor = intoRelationDesc;
 
-	return tupType;
+	queryDesc->tupDesc = tupType;
+	queryDesc->planstate = planstate;
 }
 
 /*
@@ -816,11 +795,11 @@ initResultRelInfo(ResultRelInfo *resultRelInfo,
 /* ----------------------------------------------------------------
  *		EndPlan
  *
- *		Cleans up the query plan -- closes files and free up storages
+ *		Cleans up the query plan -- closes files and frees up storage
  * ----------------------------------------------------------------
  */
 static void
-EndPlan(Plan *plan, EState *estate)
+EndPlan(PlanState *planstate, EState *estate)
 {
 	ResultRelInfo *resultRelInfo;
 	int			i;
@@ -835,7 +814,7 @@ EndPlan(Plan *plan, EState *estate)
 	/*
 	 * shut down the node-type-specific query processing
 	 */
-	ExecEndNode(plan, NULL);
+	ExecEndNode(planstate);
 
 	/*
 	 * destroy the executor "tuple" table.
@@ -902,7 +881,7 @@ EndPlan(Plan *plan, EState *estate)
  */
 static TupleTableSlot *
 ExecutePlan(EState *estate,
-			Plan *plan,
+			PlanState *planstate,
 			CmdType operation,
 			long numberTuples,
 			ScanDirection direction,
@@ -964,10 +943,10 @@ lnext:	;
 		{
 			slot = EvalPlanQualNext(estate);
 			if (TupIsNull(slot))
-				slot = ExecProcNode(plan, NULL);
+				slot = ExecProcNode(planstate);
 		}
 		else
-			slot = ExecProcNode(plan, NULL);
+			slot = ExecProcNode(planstate);
 
 		/*
 		 * if the tuple is null, then we assume there is nothing more to
@@ -1765,7 +1744,7 @@ EvalPlanQual(EState *estate, Index rti, ItemPointer tid)
 			oldepq = (evalPlanQual *) epqstate->es_evalPlanQual;
 			Assert(oldepq->rti != 0);
 			/* stop execution */
-			ExecEndNode(epq->plan, NULL);
+			ExecEndNode(epq->planstate);
 			ExecDropTupleTable(epqstate->es_tupleTable, true);
 			epqstate->es_tupleTable = NULL;
 			heap_freetuple(epqstate->es_evTuple[epq->rti - 1]);
@@ -1793,10 +1772,8 @@ EvalPlanQual(EState *estate, Index rti, ItemPointer tid)
 
 			/*
 			 * Each stack level has its own copy of the plan tree.	This
-			 * is wasteful, but necessary as long as plan nodes point to
-			 * exec state nodes rather than vice versa.  Note that
-			 * copyfuncs.c doesn't attempt to copy the exec state nodes,
-			 * which is a good thing in this situation.
+			 * is wasteful, but necessary until plan trees are fully
+			 * read-only.
 			 */
 			newepq->plan = copyObject(estate->es_origPlan);
 
@@ -1858,7 +1835,7 @@ EvalPlanQual(EState *estate, Index rti, ItemPointer tid)
 	if (endNode)
 	{
 		/* stop execution */
-		ExecEndNode(epq->plan, NULL);
+		ExecEndNode(epq->planstate);
 		ExecDropTupleTable(epqstate->es_tupleTable, true);
 		epqstate->es_tupleTable = NULL;
 	}
@@ -1886,7 +1863,7 @@ EvalPlanQual(EState *estate, Index rti, ItemPointer tid)
 	epqstate->es_tupleTable =
 		ExecCreateTupleTable(estate->es_tupleTable->size);
 
-	ExecInitNode(epq->plan, epqstate, NULL);
+	epq->planstate = ExecInitNode(epq->plan, epqstate);
 
 	return EvalPlanQualNext(estate);
 }
@@ -1902,7 +1879,7 @@ EvalPlanQualNext(EState *estate)
 	Assert(epq->rti != 0);
 
 lpqnext:;
-	slot = ExecProcNode(epq->plan, NULL);
+	slot = ExecProcNode(epq->planstate);
 
 	/*
 	 * No more tuples for this PQ. Continue previous one.
@@ -1910,7 +1887,7 @@ lpqnext:;
 	if (TupIsNull(slot))
 	{
 		/* stop execution */
-		ExecEndNode(epq->plan, NULL);
+		ExecEndNode(epq->planstate);
 		ExecDropTupleTable(epqstate->es_tupleTable, true);
 		epqstate->es_tupleTable = NULL;
 		heap_freetuple(epqstate->es_evTuple[epq->rti - 1]);
@@ -1951,7 +1928,7 @@ EndEvalPlanQual(EState *estate)
 	for (;;)
 	{
 		/* stop execution */
-		ExecEndNode(epq->plan, NULL);
+		ExecEndNode(epq->planstate);
 		ExecDropTupleTable(epqstate->es_tupleTable, true);
 		epqstate->es_tupleTable = NULL;
 		if (epqstate->es_evTuple[epq->rti - 1] != NULL)
diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index 2054a6cf059..2db4a146bc1 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -5,23 +5,23 @@
  *	 "get a tuple", and "cleanup" routines for the given node type.
  *	 If the node has children, then it will presumably call ExecInitNode,
  *	 ExecProcNode, or ExecEndNode on its subnodes and do the appropriate
- *	 processing..
+ *	 processing.
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.30 2002/06/20 20:29:27 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.31 2002/12/05 15:50:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 /*
  *	 INTERFACE ROUTINES
+ *		ExecCountSlotsNode -	count tuple slots needed by plan tree
  *		ExecInitNode	-		initialize a plan node and its subplans
  *		ExecProcNode	-		get a tuple by executing the plan node
  *		ExecEndNode		-		shut down a plan node and its subplans
- *		ExecCountSlotsNode -	count tuple slots needed by plan tree
  *		ExecGetTupType	-		get result tuple type of a plan node
  *
  *	 NOTES
@@ -53,10 +53,12 @@
  *	  * ExecInitNode() notices that it is looking at a nest loop and
  *		as the code below demonstrates, it calls ExecInitNestLoop().
  *		Eventually this calls ExecInitNode() on the right and left subplans
- *		and so forth until the entire plan is initialized.
+ *		and so forth until the entire plan is initialized.  The result
+ *		of ExecInitNode() is a plan state tree built with the same structure
+ *		as the underlying plan tree.
  *
- *	  * Then when ExecRun() is called, it calls ExecutePlan() which
- *		calls ExecProcNode() repeatedly on the top node of the plan.
+ *	  * Then when ExecRun() is called, it calls ExecutePlan() which calls
+ *		ExecProcNode() repeatedly on the top node of the plan state tree.
  *		Each time this happens, ExecProcNode() will end up calling
  *		ExecNestLoop(), which calls ExecProcNode() on its subplans.
  *		Each of these subplans is a sequential scan so ExecSeqScan() is
@@ -73,7 +75,6 @@
  *		ExecInitNode(), ExecProcNode() and ExecEndNode() dispatch
  *		their work to the appopriate node support routines which may
  *		in turn call these routines themselves on their subplans.
- *
  */
 #include "postgres.h"
 
@@ -81,11 +82,11 @@
 #include "executor/instrument.h"
 #include "executor/nodeAgg.h"
 #include "executor/nodeAppend.h"
+#include "executor/nodeFunctionscan.h"
 #include "executor/nodeGroup.h"
 #include "executor/nodeHash.h"
 #include "executor/nodeHashjoin.h"
 #include "executor/nodeIndexscan.h"
-#include "executor/nodeTidscan.h"
 #include "executor/nodeLimit.h"
 #include "executor/nodeMaterial.h"
 #include "executor/nodeMergejoin.h"
@@ -96,7 +97,7 @@
 #include "executor/nodeSort.h"
 #include "executor/nodeSubplan.h"
 #include "executor/nodeSubqueryscan.h"
-#include "executor/nodeFunctionscan.h"
+#include "executor/nodeTidscan.h"
 #include "executor/nodeUnique.h"
 #include "miscadmin.h"
 #include "tcop/tcopprot.h"
@@ -109,32 +110,23 @@
  *
  *		Initial States:
  *		  'node' is the plan produced by the query planner
+ *		  'estate' is the shared execution state for the query tree
  *
- *		returns TRUE/FALSE on whether the plan was successfully initialized
+ *		Returns a PlanState node corresponding to the given Plan node.
  * ------------------------------------------------------------------------
  */
-bool
-ExecInitNode(Plan *node, EState *estate, Plan *parent)
+PlanState *
+ExecInitNode(Plan *node, EState *estate)
 {
-	bool		result;
+	PlanState  *result;
+	List	   *subps;
 	List	   *subp;
 
 	/*
 	 * do nothing when we get to the end of a leaf on tree.
 	 */
 	if (node == NULL)
-		return FALSE;
-
-	/* Set up instrumentation for this node if the parent has it */
-	if (!node->instrument && parent && parent->instrument)
-		node->instrument = InstrAlloc();
-
-	foreach(subp, node->initPlan)
-	{
-		result = ExecInitSubPlan((SubPlan *) lfirst(subp), estate, node);
-		if (result == FALSE)
-			return FALSE;
-	}
+		return NULL;
 
 	switch (nodeTag(node))
 	{
@@ -142,104 +134,124 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent)
 			 * control nodes
 			 */
 		case T_Result:
-			result = ExecInitResult((Result *) node, estate, parent);
+			result = (PlanState *) ExecInitResult((Result *) node, estate);
 			break;
 
 		case T_Append:
-			result = ExecInitAppend((Append *) node, estate, parent);
+			result = (PlanState *) ExecInitAppend((Append *) node, estate);
 			break;
 
 			/*
 			 * scan nodes
 			 */
 		case T_SeqScan:
-			result = ExecInitSeqScan((SeqScan *) node, estate, parent);
+			result = (PlanState *) ExecInitSeqScan((SeqScan *) node, estate);
 			break;
 
 		case T_IndexScan:
-			result = ExecInitIndexScan((IndexScan *) node, estate, parent);
+			result = (PlanState *) ExecInitIndexScan((IndexScan *) node, estate);
 			break;
 
 		case T_TidScan:
-			result = ExecInitTidScan((TidScan *) node, estate, parent);
+			result = (PlanState *) ExecInitTidScan((TidScan *) node, estate);
 			break;
 
 		case T_SubqueryScan:
-			result = ExecInitSubqueryScan((SubqueryScan *) node, estate,
-										  parent);
+			result = (PlanState *) ExecInitSubqueryScan((SubqueryScan *) node, estate);
 			break;
 
 		case T_FunctionScan:
-			result = ExecInitFunctionScan((FunctionScan *) node, estate,
-										  parent);
+			result = (PlanState *) ExecInitFunctionScan((FunctionScan *) node, estate);
 			break;
 
 			/*
 			 * join nodes
 			 */
 		case T_NestLoop:
-			result = ExecInitNestLoop((NestLoop *) node, estate, parent);
+			result = (PlanState *) ExecInitNestLoop((NestLoop *) node, estate);
 			break;
 
 		case T_MergeJoin:
-			result = ExecInitMergeJoin((MergeJoin *) node, estate, parent);
-			break;
-
-		case T_Hash:
-			result = ExecInitHash((Hash *) node, estate, parent);
+			result = (PlanState *) ExecInitMergeJoin((MergeJoin *) node, estate);
 			break;
 
 		case T_HashJoin:
-			result = ExecInitHashJoin((HashJoin *) node, estate, parent);
+			result = (PlanState *) ExecInitHashJoin((HashJoin *) node, estate);
 			break;
 
 			/*
 			 * materialization nodes
 			 */
 		case T_Material:
-			result = ExecInitMaterial((Material *) node, estate, parent);
+			result = (PlanState *) ExecInitMaterial((Material *) node, estate);
 			break;
 
 		case T_Sort:
-			result = ExecInitSort((Sort *) node, estate, parent);
+			result = (PlanState *) ExecInitSort((Sort *) node, estate);
 			break;
 
-		case T_Unique:
-			result = ExecInitUnique((Unique *) node, estate, parent);
+		case T_Group:
+			result = (PlanState *) ExecInitGroup((Group *) node, estate);
 			break;
 
-		case T_SetOp:
-			result = ExecInitSetOp((SetOp *) node, estate, parent);
+		case T_Agg:
+			result = (PlanState *) ExecInitAgg((Agg *) node, estate);
 			break;
 
-		case T_Limit:
-			result = ExecInitLimit((Limit *) node, estate, parent);
+		case T_Unique:
+			result = (PlanState *) ExecInitUnique((Unique *) node, estate);
 			break;
 
-		case T_Group:
-			result = ExecInitGroup((Group *) node, estate, parent);
+		case T_Hash:
+			result = (PlanState *) ExecInitHash((Hash *) node, estate);
 			break;
 
-		case T_Agg:
-			result = ExecInitAgg((Agg *) node, estate, parent);
+		case T_SetOp:
+			result = (PlanState *) ExecInitSetOp((SetOp *) node, estate);
+			break;
+
+		case T_Limit:
+			result = (PlanState *) ExecInitLimit((Limit *) node, estate);
 			break;
 
 		default:
 			elog(ERROR, "ExecInitNode: node type %d unsupported",
 				 (int) nodeTag(node));
-			result = FALSE;
+			result = NULL;		/* keep compiler quiet */
 			break;
 	}
 
-	if (result != FALSE)
+	/*
+	 * Initialize any initPlans present in this node.  The planner put
+	 * them in a separate list for us.
+	 */
+	subps = NIL;
+	foreach(subp, node->initPlan)
 	{
-		foreach(subp, node->subPlan)
-		{
-			result = ExecInitSubPlan((SubPlan *) lfirst(subp), estate, node);
-			if (result == FALSE)
-				return FALSE;
-		}
+		SubPlan	   *subplan = (SubPlan *) lfirst(subp);
+
+		Assert(IsA(subplan, SubPlan));
+		subps = lappend(subps, ExecInitSubPlan(subplan, estate));
 	}
+	result->initPlan = subps;
+
+	/*
+	 * Initialize any subPlans present in this node.  These were found
+	 * by ExecInitExpr during initialization of the PlanState.
+	 */
+	subps = NIL;
+	foreach(subp, result->subPlan)
+	{
+		SubPlan	   *subplan = (SubPlan *) lfirst(subp);
+
+		Assert(IsA(subplan, SubPlan));
+		subps = lappend(subps, ExecInitSubPlan(subplan, estate));
+	}
+	result->subPlan = subps;
+
+	/* Set up instrumentation for this node if requested */
+	if (estate->es_instrument)
+		result->instrument = InstrAlloc();
 
 	return result;
 }
@@ -248,12 +260,11 @@ ExecInitNode(Plan *node, EState *estate, Plan *parent)
 /* ----------------------------------------------------------------
  *		ExecProcNode
  *
- *		Initial States:
- *		  the query tree must be initialized once by calling ExecInit.
+ *		Execute the given node to return a(nother) tuple.
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
-ExecProcNode(Plan *node, Plan *parent)
+ExecProcNode(PlanState *node)
 {
 	TupleTableSlot *result;
 
@@ -265,8 +276,8 @@ ExecProcNode(Plan *node, Plan *parent)
 	if (node == NULL)
 		return NULL;
 
-	if (node->chgParam != NULL) /* something changed */
-		ExecReScan(node, NULL, parent); /* let ReScan handle this */
+	if (node->chgParam != NIL)	/* something changed */
+		ExecReScan(node, NULL); /* let ReScan handle this */
 
 	if (node->instrument)
 		InstrStartNode(node->instrument);
@@ -276,85 +287,85 @@ ExecProcNode(Plan *node, Plan *parent)
 			/*
 			 * control nodes
 			 */
-		case T_Result:
-			result = ExecResult((Result *) node);
+		case T_ResultState:
+			result = ExecResult((ResultState *) node);
 			break;
 
-		case T_Append:
-			result = ExecProcAppend((Append *) node);
+		case T_AppendState:
+			result = ExecProcAppend((AppendState *) node);
 			break;
 
 			/*
 			 * scan nodes
 			 */
-		case T_SeqScan:
-			result = ExecSeqScan((SeqScan *) node);
+		case T_SeqScanState:
+			result = ExecSeqScan((SeqScanState *) node);
 			break;
 
-		case T_IndexScan:
-			result = ExecIndexScan((IndexScan *) node);
+		case T_IndexScanState:
+			result = ExecIndexScan((IndexScanState *) node);
 			break;
 
-		case T_TidScan:
-			result = ExecTidScan((TidScan *) node);
+		case T_TidScanState:
+			result = ExecTidScan((TidScanState *) node);
 			break;
 
-		case T_SubqueryScan:
-			result = ExecSubqueryScan((SubqueryScan *) node);
+		case T_SubqueryScanState:
+			result = ExecSubqueryScan((SubqueryScanState *) node);
 			break;
 
-		case T_FunctionScan:
-			result = ExecFunctionScan((FunctionScan *) node);
+		case T_FunctionScanState:
+			result = ExecFunctionScan((FunctionScanState *) node);
 			break;
 
 			/*
 			 * join nodes
 			 */
-		case T_NestLoop:
-			result = ExecNestLoop((NestLoop *) node);
+		case T_NestLoopState:
+			result = ExecNestLoop((NestLoopState *) node);
 			break;
 
-		case T_MergeJoin:
-			result = ExecMergeJoin((MergeJoin *) node);
+		case T_MergeJoinState:
+			result = ExecMergeJoin((MergeJoinState *) node);
 			break;
 
-		case T_Hash:
-			result = ExecHash((Hash *) node);
-			break;
-
-		case T_HashJoin:
-			result = ExecHashJoin((HashJoin *) node);
+		case T_HashJoinState:
+			result = ExecHashJoin((HashJoinState *) node);
 			break;
 
 			/*
 			 * materialization nodes
 			 */
-		case T_Material:
-			result = ExecMaterial((Material *) node);
+		case T_MaterialState:
+			result = ExecMaterial((MaterialState *) node);
 			break;
 
-		case T_Sort:
-			result = ExecSort((Sort *) node);
+		case T_SortState:
+			result = ExecSort((SortState *) node);
 			break;
 
-		case T_Unique:
-			result = ExecUnique((Unique *) node);
+		case T_GroupState:
+			result = ExecGroup((GroupState *) node);
 			break;
 
-		case T_SetOp:
-			result = ExecSetOp((SetOp *) node);
+		case T_AggState:
+			result = ExecAgg((AggState *) node);
 			break;
 
-		case T_Limit:
-			result = ExecLimit((Limit *) node);
+		case T_UniqueState:
+			result = ExecUnique((UniqueState *) node);
 			break;
 
-		case T_Group:
-			result = ExecGroup((Group *) node);
+		case T_HashState:
+			result = ExecHash((HashState *) node);
 			break;
 
-		case T_Agg:
-			result = ExecAgg((Agg *) node);
+		case T_SetOpState:
+			result = ExecSetOp((SetOpState *) node);
+			break;
+
+		case T_LimitState:
+			result = ExecLimit((LimitState *) node);
 			break;
 
 		default:
@@ -370,10 +381,16 @@ ExecProcNode(Plan *node, Plan *parent)
 	return result;
 }
 
+/*
+ * ExecCountSlotsNode - count up the number of tuple table slots needed
+ *
+ * Note that this scans a Plan tree, not a PlanState tree, because we
+ * haven't built the PlanState tree yet ...
+ */
 int
 ExecCountSlotsNode(Plan *node)
 {
-	if (node == (Plan *) NULL)
+	if (node == NULL)
 		return 0;
 
 	switch (nodeTag(node))
@@ -414,9 +431,6 @@ ExecCountSlotsNode(Plan *node)
 		case T_MergeJoin:
 			return ExecCountSlotsMergeJoin((MergeJoin *) node);
 
-		case T_Hash:
-			return ExecCountSlotsHash((Hash *) node);
-
 		case T_HashJoin:
 			return ExecCountSlotsHashJoin((HashJoin *) node);
 
@@ -429,26 +443,30 @@ ExecCountSlotsNode(Plan *node)
 		case T_Sort:
 			return ExecCountSlotsSort((Sort *) node);
 
+		case T_Group:
+			return ExecCountSlotsGroup((Group *) node);
+
+		case T_Agg:
+			return ExecCountSlotsAgg((Agg *) node);
+
 		case T_Unique:
 			return ExecCountSlotsUnique((Unique *) node);
 
+		case T_Hash:
+			return ExecCountSlotsHash((Hash *) node);
+
 		case T_SetOp:
 			return ExecCountSlotsSetOp((SetOp *) node);
 
 		case T_Limit:
 			return ExecCountSlotsLimit((Limit *) node);
 
-		case T_Group:
-			return ExecCountSlotsGroup((Group *) node);
-
-		case T_Agg:
-			return ExecCountSlotsAgg((Agg *) node);
-
 		default:
 			elog(ERROR, "ExecCountSlotsNode: node type %d unsupported",
 				 (int) nodeTag(node));
 			break;
 	}
+
 	return 0;
 }
 
@@ -464,7 +482,7 @@ ExecCountSlotsNode(Plan *node)
  * ----------------------------------------------------------------
  */
 void
-ExecEndNode(Plan *node, Plan *parent)
+ExecEndNode(PlanState *node)
 {
 	List	   *subp;
 
@@ -474,14 +492,19 @@ ExecEndNode(Plan *node, Plan *parent)
 	if (node == NULL)
 		return;
 
+	if (node->instrument)
+		InstrEndLoop(node->instrument);
+
+	/* Clean up initPlans and subPlans */
 	foreach(subp, node->initPlan)
-		ExecEndSubPlan((SubPlan *) lfirst(subp));
+		ExecEndSubPlan((SubPlanState *) lfirst(subp));
 	foreach(subp, node->subPlan)
-		ExecEndSubPlan((SubPlan *) lfirst(subp));
-	if (node->chgParam != NULL)
+		ExecEndSubPlan((SubPlanState *) lfirst(subp));
+
+	if (node->chgParam != NIL)
 	{
 		freeList(node->chgParam);
-		node->chgParam = NULL;
+		node->chgParam = NIL;
 	}
 
 	switch (nodeTag(node))
@@ -489,85 +512,85 @@ ExecEndNode(Plan *node, Plan *parent)
 			/*
 			 * control nodes
 			 */
-		case T_Result:
-			ExecEndResult((Result *) node);
+		case T_ResultState:
+			ExecEndResult((ResultState *) node);
 			break;
 
-		case T_Append:
-			ExecEndAppend((Append *) node);
+		case T_AppendState:
+			ExecEndAppend((AppendState *) node);
 			break;
 
 			/*
 			 * scan nodes
 			 */
-		case T_SeqScan:
-			ExecEndSeqScan((SeqScan *) node);
+		case T_SeqScanState:
+			ExecEndSeqScan((SeqScanState *) node);
 			break;
 
-		case T_IndexScan:
-			ExecEndIndexScan((IndexScan *) node);
+		case T_IndexScanState:
+			ExecEndIndexScan((IndexScanState *) node);
 			break;
 
-		case T_TidScan:
-			ExecEndTidScan((TidScan *) node);
+		case T_TidScanState:
+			ExecEndTidScan((TidScanState *) node);
 			break;
 
-		case T_SubqueryScan:
-			ExecEndSubqueryScan((SubqueryScan *) node);
+		case T_SubqueryScanState:
+			ExecEndSubqueryScan((SubqueryScanState *) node);
 			break;
 
-		case T_FunctionScan:
-			ExecEndFunctionScan((FunctionScan *) node);
+		case T_FunctionScanState:
+			ExecEndFunctionScan((FunctionScanState *) node);
 			break;
 
 			/*
 			 * join nodes
 			 */
-		case T_NestLoop:
-			ExecEndNestLoop((NestLoop *) node);
-			break;
-
-		case T_MergeJoin:
-			ExecEndMergeJoin((MergeJoin *) node);
+		case T_NestLoopState:
+			ExecEndNestLoop((NestLoopState *) node);
 			break;
 
-		case T_Hash:
-			ExecEndHash((Hash *) node);
+		case T_MergeJoinState:
+			ExecEndMergeJoin((MergeJoinState *) node);
 			break;
 
-		case T_HashJoin:
-			ExecEndHashJoin((HashJoin *) node);
+		case T_HashJoinState:
+			ExecEndHashJoin((HashJoinState *) node);
 			break;
 
 			/*
 			 * materialization nodes
 			 */
-		case T_Material:
-			ExecEndMaterial((Material *) node);
+		case T_MaterialState:
+			ExecEndMaterial((MaterialState *) node);
 			break;
 
-		case T_Sort:
-			ExecEndSort((Sort *) node);
+		case T_SortState:
+			ExecEndSort((SortState *) node);
 			break;
 
-		case T_Unique:
-			ExecEndUnique((Unique *) node);
+		case T_GroupState:
+			ExecEndGroup((GroupState *) node);
 			break;
 
-		case T_SetOp:
-			ExecEndSetOp((SetOp *) node);
+		case T_AggState:
+			ExecEndAgg((AggState *) node);
 			break;
 
-		case T_Limit:
-			ExecEndLimit((Limit *) node);
+		case T_UniqueState:
+			ExecEndUnique((UniqueState *) node);
 			break;
 
-		case T_Group:
-			ExecEndGroup((Group *) node);
+		case T_HashState:
+			ExecEndHash((HashState *) node);
 			break;
 
-		case T_Agg:
-			ExecEndAgg((Agg *) node);
+		case T_SetOpState:
+			ExecEndSetOp((SetOpState *) node);
+			break;
+
+		case T_LimitState:
+			ExecEndLimit((LimitState *) node);
 			break;
 
 		default:
@@ -575,9 +598,6 @@ ExecEndNode(Plan *node, Plan *parent)
 				 (int) nodeTag(node));
 			break;
 	}
-
-	if (node->instrument)
-		InstrEndLoop(node->instrument);
 }
 
 
@@ -592,7 +612,7 @@ ExecEndNode(Plan *node, Plan *parent)
  * ----------------------------------------------------------------
  */
 TupleDesc
-ExecGetTupType(Plan *node)
+ExecGetTupType(PlanState *node)
 {
 	TupleTableSlot *slot;
 
@@ -601,147 +621,147 @@ ExecGetTupType(Plan *node)
 
 	switch (nodeTag(node))
 	{
-		case T_Result:
+		case T_ResultState:
 			{
-				ResultState *resstate = ((Result *) node)->resstate;
+				ResultState *resstate = (ResultState *) node;
 
-				slot = resstate->cstate.cs_ResultTupleSlot;
+				slot = resstate->ps.ps_ResultTupleSlot;
 			}
 			break;
 
-		case T_SeqScan:
+		case T_AppendState:
 			{
-				CommonScanState *scanstate = ((SeqScan *) node)->scanstate;
+				AppendState *appendstate = (AppendState *) node;
 
-				slot = scanstate->cstate.cs_ResultTupleSlot;
+				slot = appendstate->ps.ps_ResultTupleSlot;
 			}
 			break;
 
-		case T_NestLoop:
+		case T_SeqScanState:
 			{
-				NestLoopState *nlstate = ((NestLoop *) node)->nlstate;
+				SeqScanState *scanstate = (SeqScanState *) node;
 
-				slot = nlstate->jstate.cs_ResultTupleSlot;
+				slot = scanstate->ps.ps_ResultTupleSlot;
 			}
 			break;
 
-		case T_Append:
+		case T_IndexScanState:
 			{
-				AppendState *appendstate = ((Append *) node)->appendstate;
+				IndexScanState *scanstate = (IndexScanState *) node;
 
-				slot = appendstate->cstate.cs_ResultTupleSlot;
+				slot = scanstate->ss.ps.ps_ResultTupleSlot;
 			}
 			break;
 
-		case T_IndexScan:
+		case T_TidScanState:
 			{
-				CommonScanState *scanstate = ((IndexScan *) node)->scan.scanstate;
+				TidScanState *scanstate = (TidScanState *) node;
 
-				slot = scanstate->cstate.cs_ResultTupleSlot;
+				slot = scanstate->ss.ps.ps_ResultTupleSlot;
 			}
 			break;
 
-		case T_TidScan:
+		case T_SubqueryScanState:
 			{
-				CommonScanState *scanstate = ((TidScan *) node)->scan.scanstate;
+				SubqueryScanState *scanstate = (SubqueryScanState *) node;
 
-				slot = scanstate->cstate.cs_ResultTupleSlot;
+				slot = scanstate->ss.ps.ps_ResultTupleSlot;
 			}
 			break;
 
-		case T_SubqueryScan:
+		case T_FunctionScanState:
 			{
-				CommonScanState *scanstate = ((SubqueryScan *) node)->scan.scanstate;
+				FunctionScanState *scanstate = (FunctionScanState *) node;
 
-				slot = scanstate->cstate.cs_ResultTupleSlot;
+				slot = scanstate->ss.ps.ps_ResultTupleSlot;
 			}
 			break;
 
-		case T_FunctionScan:
+		case T_NestLoopState:
 			{
-				CommonScanState *scanstate = ((FunctionScan *) node)->scan.scanstate;
+				NestLoopState *nlstate = (NestLoopState *) node;
 
-				slot = scanstate->cstate.cs_ResultTupleSlot;
+				slot = nlstate->js.ps.ps_ResultTupleSlot;
 			}
 			break;
 
-		case T_Material:
+		case T_MergeJoinState:
 			{
-				MaterialState *matstate = ((Material *) node)->matstate;
+				MergeJoinState *mergestate = (MergeJoinState *) node;
 
-				slot = matstate->csstate.css_ScanTupleSlot;
+				slot = mergestate->js.ps.ps_ResultTupleSlot;
 			}
 			break;
 
-		case T_Sort:
+		case T_HashJoinState:
 			{
-				SortState  *sortstate = ((Sort *) node)->sortstate;
+				HashJoinState *hashjoinstate = (HashJoinState *) node;
 
-				slot = sortstate->csstate.css_ScanTupleSlot;
+				slot = hashjoinstate->js.ps.ps_ResultTupleSlot;
 			}
 			break;
 
-		case T_Agg:
+		case T_MaterialState:
 			{
-				AggState   *aggstate = ((Agg *) node)->aggstate;
+				MaterialState *matstate = (MaterialState *) node;
 
-				slot = aggstate->csstate.cstate.cs_ResultTupleSlot;
+				slot = matstate->ss.ss_ScanTupleSlot;
 			}
 			break;
 
-		case T_Group:
+		case T_SortState:
 			{
-				GroupState *grpstate = ((Group *) node)->grpstate;
+				SortState  *sortstate = (SortState *) node;
 
-				slot = grpstate->csstate.cstate.cs_ResultTupleSlot;
+				slot = sortstate->ss.ss_ScanTupleSlot;
 			}
 			break;
 
-		case T_Hash:
+		case T_GroupState:
 			{
-				HashState  *hashstate = ((Hash *) node)->hashstate;
+				GroupState *grpstate = (GroupState *) node;
 
-				slot = hashstate->cstate.cs_ResultTupleSlot;
+				slot = grpstate->ss.ps.ps_ResultTupleSlot;
 			}
 			break;
 
-		case T_Unique:
+		case T_AggState:
 			{
-				UniqueState *uniquestate = ((Unique *) node)->uniquestate;
+				AggState   *aggstate = (AggState *) node;
 
-				slot = uniquestate->cstate.cs_ResultTupleSlot;
+				slot = aggstate->ss.ps.ps_ResultTupleSlot;
 			}
 			break;
 
-		case T_SetOp:
+		case T_UniqueState:
 			{
-				SetOpState *setopstate = ((SetOp *) node)->setopstate;
+				UniqueState *uniquestate = (UniqueState *) node;
 
-				slot = setopstate->cstate.cs_ResultTupleSlot;
+				slot = uniquestate->ps.ps_ResultTupleSlot;
 			}
 			break;
 
-		case T_Limit:
+		case T_HashState:
 			{
-				LimitState *limitstate = ((Limit *) node)->limitstate;
+				HashState  *hashstate = (HashState *) node;
 
-				slot = limitstate->cstate.cs_ResultTupleSlot;
+				slot = hashstate->ps.ps_ResultTupleSlot;
 			}
 			break;
 
-		case T_MergeJoin:
+		case T_SetOpState:
 			{
-				MergeJoinState *mergestate = ((MergeJoin *) node)->mergestate;
+				SetOpState *setopstate = (SetOpState *) node;
 
-				slot = mergestate->jstate.cs_ResultTupleSlot;
+				slot = setopstate->ps.ps_ResultTupleSlot;
 			}
 			break;
 
-		case T_HashJoin:
+		case T_LimitState:
 			{
-				HashJoinState *hashjoinstate = ((HashJoin *) node)->hashjoinstate;
+				LimitState *limitstate = (LimitState *) node;
 
-				slot = hashjoinstate->jstate.cs_ResultTupleSlot;
+				slot = limitstate->ps.ps_ResultTupleSlot;
 			}
 			break;
 
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index cb31c025df2..e7c724c66ed 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.112 2002/12/01 20:27:32 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.113 2002/12/05 15:50:31 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1766,7 +1766,8 @@ ExecEvalExpr(Node *expression,
 													isNull, isDone);
 						break;
 					case SUBPLAN_EXPR:
-						retDatum = ExecSubPlan((SubPlan *) expr->oper,
+						/* XXX temporary hack to find exec state node */
+						retDatum = ExecSubPlan(((SubPlan *) expr->oper)->pstate,
 											   expr->args, econtext,
 											   isNull);
 						break;
@@ -1850,6 +1851,169 @@ ExecEvalExprSwitchContext(Node *expression,
 }
 
 
+/*
+ * ExecInitExpr: prepare an expression tree for execution
+ *
+ *	'node' is the root of the expression tree to examine
+ *	'parent' is the PlanState node that owns the expression,
+ *		or NULL if we are preparing an expression that is not associated
+ *		with a plan.  (If so, it can't have Aggrefs or SubPlans.)
+ *
+ * Soon this will generate an expression state tree paralleling the given
+ * expression tree.  Right now, it just searches the expression tree for
+ * Aggref and SubPlan nodes.
+ */
+Node *
+ExecInitExpr(Node *node, PlanState *parent)
+{
+	List	   *temp;
+
+	if (node == NULL)
+		return NULL;
+	switch (nodeTag(node))
+	{
+		case T_Var:
+			break;
+		case T_Const:
+			break;
+		case T_Param:
+			break;
+		case T_Aggref:
+			if (parent && IsA(parent, AggState))
+			{
+				AggState   *aggstate = (AggState *) parent;
+				int			naggs;
+
+				aggstate->aggs = lcons(node, aggstate->aggs);
+				naggs = ++aggstate->numaggs;
+
+				ExecInitExpr(((Aggref *) node)->target, parent);
+
+				/*
+				 * Complain if the aggregate's argument contains any
+				 * aggregates; nested agg functions are semantically
+				 * nonsensical.  (This probably was caught earlier,
+				 * but we defend against it here anyway.)
+				 */
+				if (naggs != aggstate->numaggs)
+					elog(ERROR, "Aggregate function calls may not be nested");
+			}
+			else
+				elog(ERROR, "ExecInitExpr: Aggref not expected here");
+			break;
+		case T_ArrayRef:
+			{
+				ArrayRef   *aref = (ArrayRef *) node;
+
+				ExecInitExpr((Node *) aref->refupperindexpr, parent);
+				ExecInitExpr((Node *) aref->reflowerindexpr, parent);
+				ExecInitExpr(aref->refexpr, parent);
+				ExecInitExpr(aref->refassgnexpr, parent);
+			}
+			break;
+		case T_Expr:
+			{
+				Expr	   *expr = (Expr *) node;
+
+				switch (expr->opType)
+				{
+					case OP_EXPR:
+						break;
+					case FUNC_EXPR:
+						break;
+					case OR_EXPR:
+						break;
+					case AND_EXPR:
+						break;
+					case NOT_EXPR:
+						break;
+					case DISTINCT_EXPR:
+						break;
+					case SUBPLAN_EXPR:
+						if (parent)
+						{
+							SubLink *sublink = ((SubPlan *) expr->oper)->sublink;
+
+							/*
+							 * Here we just add the SubPlan nodes to
+							 * parent->subPlan.  Later they will be expanded
+							 * to SubPlanState nodes.
+							 */
+							parent->subPlan = lcons(expr->oper,
+													parent->subPlan);
+
+							/* Must recurse into oper list too */
+							Assert(IsA(sublink, SubLink));
+							if (sublink->lefthand)
+								elog(ERROR, "ExecInitExpr: sublink has not been transformed");
+							ExecInitExpr((Node *) sublink->oper, parent);
+						}
+						else
+							elog(ERROR, "ExecInitExpr: SubPlan not expected here");
+						break;
+					default:
+						elog(ERROR, "ExecInitExpr: unknown expression type %d",
+							 expr->opType);
+						break;
+				}
+				/* for all Expr node types, examine args list */
+				ExecInitExpr((Node *) expr->args, parent);
+			}
+			break;
+		case T_FieldSelect:
+			ExecInitExpr(((FieldSelect *) node)->arg, parent);
+			break;
+		case T_RelabelType:
+			ExecInitExpr(((RelabelType *) node)->arg, parent);
+			break;
+		case T_CaseExpr:
+			{
+				CaseExpr   *caseexpr = (CaseExpr *) node;
+
+				foreach(temp, caseexpr->args)
+				{
+					CaseWhen   *when = (CaseWhen *) lfirst(temp);
+
+					Assert(IsA(when, CaseWhen));
+					ExecInitExpr(when->expr, parent);
+					ExecInitExpr(when->result, parent);
+				}
+				/* caseexpr->arg should be null, but we'll check it anyway */
+				ExecInitExpr(caseexpr->arg, parent);
+				ExecInitExpr(caseexpr->defresult, parent);
+			}
+			break;
+		case T_NullTest:
+			ExecInitExpr(((NullTest *) node)->arg, parent);
+			break;
+		case T_BooleanTest:
+			ExecInitExpr(((BooleanTest *) node)->arg, parent);
+			break;
+		case T_ConstraintTest:
+			ExecInitExpr(((ConstraintTest *) node)->arg, parent);
+			ExecInitExpr(((ConstraintTest *) node)->check_expr, parent);
+			break;
+		case T_ConstraintTestValue:
+			break;
+		case T_List:
+			foreach(temp, (List *) node)
+			{
+				ExecInitExpr((Node *) lfirst(temp), parent);
+			}
+			break;
+		case T_TargetEntry:
+			ExecInitExpr(((TargetEntry *) node)->expr, parent);
+			break;
+		default:
+			elog(ERROR, "ExecInitExpr: unknown expression type %d",
+				 nodeTag(node));
+			break;
+	}
+
+	return node;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index 9fd7c4fb00c..6944e03e9bc 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.21 2002/09/02 02:47:02 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.22 2002/12/05 15:50:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,10 +44,9 @@
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
-ExecScan(Scan *node,
+ExecScan(ScanState *node,
 		 ExecScanAccessMtd accessMtd)	/* function returning a tuple */
 {
-	CommonScanState *scanstate;
 	EState	   *estate;
 	ExprContext *econtext;
 	List	   *qual;
@@ -57,23 +56,22 @@ ExecScan(Scan *node,
 	/*
 	 * Fetch data from node
 	 */
-	estate = node->plan.state;
-	scanstate = node->scanstate;
-	econtext = scanstate->cstate.cs_ExprContext;
-	qual = node->plan.qual;
+	estate = node->ps.state;
+	econtext = node->ps.ps_ExprContext;
+	qual = node->ps.qual;
 
 	/*
 	 * Check to see if we're still projecting out tuples from a previous
 	 * scan tuple (because there is a function-returning-set in the
 	 * projection expressions).  If so, try to project another one.
 	 */
-	if (scanstate->cstate.cs_TupFromTlist)
+	if (node->ps.ps_TupFromTlist)
 	{
-		resultSlot = ExecProject(scanstate->cstate.cs_ProjInfo, &isDone);
+		resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone);
 		if (isDone == ExprMultipleResult)
 			return resultSlot;
 		/* Done with that source tuple... */
-		scanstate->cstate.cs_TupFromTlist = false;
+		node->ps.ps_TupFromTlist = false;
 	}
 
 	/*
@@ -104,7 +102,7 @@ ExecScan(Scan *node,
 		if (TupIsNull(slot))
 		{
 			return ExecStoreTuple(NULL,
-								  scanstate->cstate.cs_ProjInfo->pi_slot,
+								  node->ps.ps_ProjInfo->pi_slot,
 								  InvalidBuffer,
 								  true);
 		}
@@ -130,10 +128,10 @@ ExecScan(Scan *node,
 			 * return it --- unless we find we can project no tuples from
 			 * this scan tuple, in which case continue scan.
 			 */
-			resultSlot = ExecProject(scanstate->cstate.cs_ProjInfo, &isDone);
+			resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone);
 			if (isDone != ExprEndResult)
 			{
-				scanstate->cstate.cs_TupFromTlist = (isDone == ExprMultipleResult);
+				node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
 				return resultSlot;
 			}
 		}
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index 7b0df664c74..f5a6863b1ac 100644
--- a/src/backend/executor/execTuples.c
+++ b/src/backend/executor/execTuples.c
@@ -15,7 +15,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.60 2002/09/28 20:00:19 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.61 2002/12/05 15:50:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -483,11 +483,11 @@ ExecSetSlotDescriptorIsNew(TupleTableSlot *slot,		/* slot to change */
  * ----------------
  */
 void
-ExecInitResultTupleSlot(EState *estate, CommonState *commonstate)
+ExecInitResultTupleSlot(EState *estate, PlanState *planstate)
 {
 	INIT_SLOT_DEFS;
 	INIT_SLOT_ALLOC;
-	commonstate->cs_ResultTupleSlot = slot;
+	planstate->ps_ResultTupleSlot = slot;
 }
 
 /* ----------------
@@ -495,11 +495,11 @@ ExecInitResultTupleSlot(EState *estate, CommonState *commonstate)
  * ----------------
  */
 void
-ExecInitScanTupleSlot(EState *estate, CommonScanState *commonscanstate)
+ExecInitScanTupleSlot(EState *estate, ScanState *scanstate)
 {
 	INIT_SLOT_DEFS;
 	INIT_SLOT_ALLOC;
-	commonscanstate->css_ScanTupleSlot = slot;
+	scanstate->ss_ScanTupleSlot = slot;
 }
 
 /* ----------------
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 86440d10f9e..0dd17b7965b 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.90 2002/09/04 20:31:18 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.91 2002/12/05 15:50:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -142,7 +142,7 @@ DisplayTupleCount(FILE *statfp)
  * ----------------
  */
 void
-ExecAssignExprContext(EState *estate, CommonState *commonstate)
+ExecAssignExprContext(EState *estate, PlanState *planstate)
 {
 	ExprContext *econtext = makeNode(ExprContext);
 
@@ -166,7 +166,7 @@ ExecAssignExprContext(EState *estate, CommonState *commonstate)
 	econtext->ecxt_aggnulls = NULL;
 	econtext->ecxt_callbacks = NULL;
 
-	commonstate->cs_ExprContext = econtext;
+	planstate->ps_ExprContext = econtext;
 }
 
 /* ----------------
@@ -259,10 +259,10 @@ MakePerTupleExprContext(EState *estate)
  * ----------------
  */
 void
-ExecAssignResultType(CommonState *commonstate,
+ExecAssignResultType(PlanState *planstate,
 					 TupleDesc tupDesc, bool shouldFree)
 {
-	TupleTableSlot *slot = commonstate->cs_ResultTupleSlot;
+	TupleTableSlot *slot = planstate->ps_ResultTupleSlot;
 
 	ExecSetSlotDescriptor(slot, tupDesc, shouldFree);
 }
@@ -272,15 +272,15 @@ ExecAssignResultType(CommonState *commonstate,
  * ----------------
  */
 void
-ExecAssignResultTypeFromOuterPlan(Plan *node, CommonState *commonstate)
+ExecAssignResultTypeFromOuterPlan(PlanState *planstate)
 {
-	Plan	   *outerPlan;
+	PlanState  *outerPlan;
 	TupleDesc	tupDesc;
 
-	outerPlan = outerPlan(node);
+	outerPlan = outerPlanState(planstate);
 	tupDesc = ExecGetTupType(outerPlan);
 
-	ExecAssignResultType(commonstate, tupDesc, false);
+	ExecAssignResultType(planstate, tupDesc, false);
 }
 
 /* ----------------
@@ -288,7 +288,7 @@ ExecAssignResultTypeFromOuterPlan(Plan *node, CommonState *commonstate)
  * ----------------
  */
 void
-ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate)
+ExecAssignResultTypeFromTL(PlanState *planstate)
 {
 	ResultRelInfo *ri;
 	bool		hasoid = false;
@@ -311,7 +311,7 @@ ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate)
 	 * each of the child plans of the topmost Append plan.	So, this is
 	 * ugly but it works, for now ...
 	 */
-	ri = node->state->es_result_relation_info;
+	ri = planstate->state->es_result_relation_info;
 	if (ri != NULL)
 	{
 		Relation	rel = ri->ri_RelationDesc;
@@ -320,8 +320,13 @@ ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate)
 			hasoid = rel->rd_rel->relhasoids;
 	}
 
-	tupDesc = ExecTypeFromTL(node->targetlist, hasoid);
-	ExecAssignResultType(commonstate, tupDesc, true);
+	/*
+	 * XXX Some plan nodes don't bother to set up planstate->targetlist,
+	 * so use the underlying plan's targetlist instead.  This will probably
+	 * need to be fixed later.
+	 */
+	tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid);
+	ExecAssignResultType(planstate, tupDesc, true);
 }
 
 /* ----------------
@@ -329,9 +334,9 @@ ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate)
  * ----------------
  */
 TupleDesc
-ExecGetResultType(CommonState *commonstate)
+ExecGetResultType(PlanState *planstate)
 {
-	TupleTableSlot *slot = commonstate->cs_ResultTupleSlot;
+	TupleTableSlot *slot = planstate->ps_ResultTupleSlot;
 
 	return slot->ttc_tupleDescriptor;
 }
@@ -342,23 +347,23 @@ ExecGetResultType(CommonState *commonstate)
  * ----------------
  */
 void
-ExecAssignProjectionInfo(Plan *node, CommonState *commonstate)
+ExecAssignProjectionInfo(PlanState *planstate)
 {
 	ProjectionInfo *projInfo;
 	List	   *targetList;
 	int			len;
 
-	targetList = node->targetlist;
+	targetList = planstate->targetlist;
 	len = ExecTargetListLength(targetList);
 
 	projInfo = makeNode(ProjectionInfo);
 	projInfo->pi_targetlist = targetList;
 	projInfo->pi_len = len;
 	projInfo->pi_tupValue = (len <= 0) ? NULL : (Datum *) palloc(sizeof(Datum) * len);
-	projInfo->pi_exprContext = commonstate->cs_ExprContext;
-	projInfo->pi_slot = commonstate->cs_ResultTupleSlot;
+	projInfo->pi_exprContext = planstate->ps_ExprContext;
+	projInfo->pi_slot = planstate->ps_ResultTupleSlot;
 
-	commonstate->cs_ProjInfo = projInfo;
+	planstate->ps_ProjInfo = projInfo;
 }
 
 
@@ -367,7 +372,7 @@ ExecAssignProjectionInfo(Plan *node, CommonState *commonstate)
  * ----------------
  */
 void
-ExecFreeProjectionInfo(CommonState *commonstate)
+ExecFreeProjectionInfo(PlanState *planstate)
 {
 	ProjectionInfo *projInfo;
 
@@ -375,7 +380,7 @@ ExecFreeProjectionInfo(CommonState *commonstate)
 	 * get projection info.  if NULL then this node has none so we just
 	 * return.
 	 */
-	projInfo = commonstate->cs_ProjInfo;
+	projInfo = planstate->ps_ProjInfo;
 	if (projInfo == NULL)
 		return;
 
@@ -386,7 +391,7 @@ ExecFreeProjectionInfo(CommonState *commonstate)
 		pfree(projInfo->pi_tupValue);
 
 	pfree(projInfo);
-	commonstate->cs_ProjInfo = NULL;
+	planstate->ps_ProjInfo = NULL;
 }
 
 /* ----------------
@@ -394,7 +399,7 @@ ExecFreeProjectionInfo(CommonState *commonstate)
  * ----------------
  */
 void
-ExecFreeExprContext(CommonState *commonstate)
+ExecFreeExprContext(PlanState *planstate)
 {
 	ExprContext *econtext;
 
@@ -402,7 +407,7 @@ ExecFreeExprContext(CommonState *commonstate)
 	 * get expression context.	if NULL then this node has none so we just
 	 * return.
 	 */
-	econtext = commonstate->cs_ExprContext;
+	econtext = planstate->ps_ExprContext;
 	if (econtext == NULL)
 		return;
 
@@ -416,7 +421,7 @@ ExecFreeExprContext(CommonState *commonstate)
 	 */
 	MemoryContextDelete(econtext->ecxt_per_tuple_memory);
 	pfree(econtext);
-	commonstate->cs_ExprContext = NULL;
+	planstate->ps_ExprContext = NULL;
 }
 
 /* ----------------------------------------------------------------
@@ -434,9 +439,9 @@ ExecFreeExprContext(CommonState *commonstate)
  * ----------------
  */
 TupleDesc
-ExecGetScanType(CommonScanState *csstate)
+ExecGetScanType(ScanState *scanstate)
 {
-	TupleTableSlot *slot = csstate->css_ScanTupleSlot;
+	TupleTableSlot *slot = scanstate->ss_ScanTupleSlot;
 
 	return slot->ttc_tupleDescriptor;
 }
@@ -446,10 +451,10 @@ ExecGetScanType(CommonScanState *csstate)
  * ----------------
  */
 void
-ExecAssignScanType(CommonScanState *csstate,
+ExecAssignScanType(ScanState *scanstate,
 				   TupleDesc tupDesc, bool shouldFree)
 {
-	TupleTableSlot *slot = csstate->css_ScanTupleSlot;
+	TupleTableSlot *slot = scanstate->ss_ScanTupleSlot;
 
 	ExecSetSlotDescriptor(slot, tupDesc, shouldFree);
 }
@@ -459,15 +464,15 @@ ExecAssignScanType(CommonScanState *csstate,
  * ----------------
  */
 void
-ExecAssignScanTypeFromOuterPlan(Plan *node, CommonScanState *csstate)
+ExecAssignScanTypeFromOuterPlan(ScanState *scanstate)
 {
-	Plan	   *outerPlan;
+	PlanState  *outerPlan;
 	TupleDesc	tupDesc;
 
-	outerPlan = outerPlan(node);
+	outerPlan = outerPlanState(scanstate);
 	tupDesc = ExecGetTupType(outerPlan);
 
-	ExecAssignScanType(csstate, tupDesc, false);
+	ExecAssignScanType(scanstate, tupDesc, false);
 }
 
 
@@ -718,7 +723,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 }
 
 void
-SetChangedParamList(Plan *node, List *newchg)
+SetChangedParamList(PlanState *node, List *newchg)
 {
 	List	   *nl;
 
@@ -727,8 +732,8 @@ SetChangedParamList(Plan *node, List *newchg)
 		int			paramId = lfirsti(nl);
 
 		/* if this node doesn't depend on a param ... */
-		if (!intMember(paramId, node->extParam) &&
-			!intMember(paramId, node->locParam))
+		if (!intMember(paramId, node->plan->extParam) &&
+			!intMember(paramId, node->plan->locParam))
 			continue;
 		/* if this param is already in list of changed ones ... */
 		if (intMember(paramId, node->chgParam))
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 71166d9096c..328aea5f079 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.60 2002/11/13 00:39:47 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.61 2002/12/05 15:50:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,7 +28,9 @@
 
 
 /*
- * We have an execution_state record for each query in a function.
+ * We have an execution_state record for each query in a function.  Each
+ * record contains a querytree and plantree for its query.  If the query
+ * is currently in F_EXEC_RUN state then there's a QueryDesc too.
  */
 typedef enum
 {
@@ -37,10 +39,11 @@ typedef enum
 
 typedef struct local_es
 {
-	QueryDesc  *qd;
-	EState	   *estate;
 	struct local_es *next;
 	ExecStatus	status;
+	Query	   *query;
+	Plan	   *plan;
+	QueryDesc  *qd;				/* null unless status == RUN */
 } execution_state;
 
 #define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *) NULL)
@@ -62,6 +65,8 @@ typedef struct
 								 * we end execution of the function and
 								 * free stuff */
 
+	ParamListInfo paramLI;		/* Param list representing current args */
+
 	/* head of linked list of execution_state records */
 	execution_state *func_state;
 } SQLFunctionCache;
@@ -73,10 +78,11 @@ typedef SQLFunctionCache *SQLFunctionCachePtr;
 static execution_state *init_execution_state(char *src,
 					 Oid *argOidVect, int nargs);
 static void init_sql_fcache(FmgrInfo *finfo);
-static void postquel_start(execution_state *es);
+static void postquel_start(execution_state *es, SQLFunctionCachePtr fcache);
 static TupleTableSlot *postquel_getnext(execution_state *es);
 static void postquel_end(execution_state *es);
-static void postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo);
+static void postquel_sub_params(SQLFunctionCachePtr fcache,
+								FunctionCallInfo fcinfo);
 static Datum postquel_execute(execution_state *es,
 				 FunctionCallInfo fcinfo,
 				 SQLFunctionCachePtr fcache);
@@ -101,7 +107,6 @@ init_execution_state(char *src, Oid *argOidVect, int nargs)
 		Query	   *queryTree = lfirst(qtl_item);
 		Plan	   *planTree;
 		execution_state *newes;
-		EState	   *estate;
 
 		planTree = pg_plan_query(queryTree);
 
@@ -113,29 +118,9 @@ init_execution_state(char *src, Oid *argOidVect, int nargs)
 
 		newes->next = NULL;
 		newes->status = F_EXEC_START;
-		newes->qd = CreateQueryDesc(queryTree, planTree, None, NULL);
-		newes->estate = estate = CreateExecutorState();
-
-		if (nargs > 0)
-		{
-			int			i;
-			ParamListInfo paramLI;
-
-			paramLI = (ParamListInfo) palloc0((nargs + 1) * sizeof(ParamListInfoData));
-
-			estate->es_param_list_info = paramLI;
-
-			for (i = 0; i < nargs; paramLI++, i++)
-			{
-				paramLI->kind = PARAM_NUM;
-				paramLI->id = i + 1;
-				paramLI->isnull = false;
-				paramLI->value = (Datum) NULL;
-			}
-			paramLI->kind = PARAM_INVALID;
-		}
-		else
-			estate->es_param_list_info = (ParamListInfo) NULL;
+		newes->query = queryTree;
+		newes->plan = planTree;
+		newes->qd = NULL;
 
 		preves = newes;
 	}
@@ -219,6 +204,10 @@ init_sql_fcache(FmgrInfo *finfo)
 	else
 		fcache->funcSlot = NULL;
 
+	/*
+	 * Parse and plan the queries.  We need the argument info to pass
+	 * to the parser.
+	 */
 	nargs = procedureStruct->pronargs;
 
 	if (nargs > 0)
@@ -252,15 +241,18 @@ init_sql_fcache(FmgrInfo *finfo)
 
 
 static void
-postquel_start(execution_state *es)
+postquel_start(execution_state *es, SQLFunctionCachePtr fcache)
 {
-	/*
-	 * Do nothing for utility commands. (create, destroy...)  DZ -
-	 * 30-8-1996
-	 */
-	if (es->qd->operation == CMD_UTILITY)
-		return;
-	ExecutorStart(es->qd, es->estate);
+	Assert(es->qd == NULL);
+	es->qd = CreateQueryDesc(es->query, es->plan,
+							 None, NULL,
+							 fcache->paramLI, false);
+
+	/* Utility commands don't need Executor. */
+	if (es->qd->operation != CMD_UTILITY)
+		ExecutorStart(es->qd);
+
+	es->status = F_EXEC_RUN;
 }
 
 static TupleTableSlot *
@@ -282,40 +274,52 @@ postquel_getnext(execution_state *es)
 	/* If it's not the last command, just run it to completion */
 	count = (LAST_POSTQUEL_COMMAND(es)) ? 1L : 0L;
 
-	return ExecutorRun(es->qd, es->estate, ForwardScanDirection, count);
+	return ExecutorRun(es->qd, ForwardScanDirection, count);
 }
 
 static void
 postquel_end(execution_state *es)
 {
-	/*
-	 * Do nothing for utility commands. (create, destroy...)  DZ -
-	 * 30-8-1996
-	 */
-	if (es->qd->operation == CMD_UTILITY)
-		return;
-	ExecutorEnd(es->qd, es->estate);
+	/* Utility commands don't need Executor. */
+	if (es->qd->operation != CMD_UTILITY)
+		ExecutorEnd(es->qd);
+
+	pfree(es->qd);
+	es->qd = NULL;
+
+	es->status = F_EXEC_DONE;
 }
 
+/* Build ParamListInfo array representing current arguments */
 static void
-postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo)
+postquel_sub_params(SQLFunctionCachePtr fcache,
+					FunctionCallInfo fcinfo)
 {
-	EState	   *estate;
 	ParamListInfo paramLI;
+	int			nargs = fcinfo->nargs;
 
-	estate = es->estate;
-	paramLI = estate->es_param_list_info;
-
-	while (paramLI->kind != PARAM_INVALID)
+	if (nargs > 0)
 	{
-		if (paramLI->kind == PARAM_NUM)
+		int			i;
+
+		paramLI = (ParamListInfo) palloc0((nargs + 1) * sizeof(ParamListInfoData));
+
+		for (i = 0; i < nargs; i++)
 		{
-			Assert(paramLI->id <= fcinfo->nargs);
-			paramLI->value = fcinfo->arg[paramLI->id - 1];
-			paramLI->isnull = fcinfo->argnull[paramLI->id - 1];
+			paramLI[i].kind = PARAM_NUM;
+			paramLI[i].id = i + 1;
+			paramLI[i].value = fcinfo->arg[i];
+			paramLI[i].isnull = fcinfo->argnull[i];
 		}
-		paramLI++;
+		paramLI[nargs].kind = PARAM_INVALID;
 	}
+	else
+		paramLI = (ParamListInfo) NULL;
+
+	if (fcache->paramLI)
+		pfree(fcache->paramLI);
+
+	fcache->paramLI = paramLI;
 }
 
 static TupleTableSlot *
@@ -359,27 +363,14 @@ postquel_execute(execution_state *es,
 	TupleTableSlot *slot;
 	Datum		value;
 
-	/*
-	 * It's more right place to do it (before
-	 * postquel_start->ExecutorStart). Now
-	 * ExecutorStart->ExecInitIndexScan->ExecEvalParam works ok. (But
-	 * note: I HOPE we can do it here). - vadim 01/22/97
-	 */
-	if (fcinfo->nargs > 0)
-		postquel_sub_params(es, fcinfo);
-
 	if (es->status == F_EXEC_START)
-	{
-		postquel_start(es);
-		es->status = F_EXEC_RUN;
-	}
+		postquel_start(es, fcache);
 
 	slot = postquel_getnext(es);
 
 	if (TupIsNull(slot))
 	{
 		postquel_end(es);
-		es->status = F_EXEC_DONE;
 		fcinfo->isnull = true;
 
 		/*
@@ -438,10 +429,7 @@ postquel_execute(execution_state *es,
 		 * execution now.
 		 */
 		if (!fcinfo->flinfo->fn_retset)
-		{
 			postquel_end(es);
-			es->status = F_EXEC_DONE;
-		}
 
 		return value;
 	}
@@ -471,7 +459,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
 	oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
 
 	/*
-	 * Initialize fcache and execution state if first time through.
+	 * Initialize fcache (build plans) if first time through.
 	 */
 	fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra;
 	if (fcache == NULL)
@@ -481,6 +469,13 @@ fmgr_sql(PG_FUNCTION_ARGS)
 	}
 	es = fcache->func_state;
 
+	/*
+	 * Convert params to appropriate format if starting a fresh execution.
+	 * (If continuing execution, we can re-use prior params.)
+	 */
+	if (es && es->status == F_EXEC_START)
+		postquel_sub_params(fcache, fcinfo);
+
 	/*
 	 * Find first unfinished query in function.
 	 */
@@ -506,7 +501,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
 	if (es == (execution_state *) NULL)
 	{
 		/*
-		 * Reset the execution states to start over again
+		 * Reset the execution states to start over again on next call.
 		 */
 		es = fcache->func_state;
 		while (es)
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 82e9f48e1e0..bdbe61cb10a 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -45,7 +45,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.97 2002/11/29 21:39:11 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.98 2002/12/05 15:50:32 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -212,11 +212,12 @@ static void finalize_aggregate(AggState *aggstate,
 							   AggStatePerAgg peraggstate,
 							   AggStatePerGroup pergroupstate,
 							   Datum *resultVal, bool *resultIsNull);
-static void build_hash_table(Agg *node);
-static AggHashEntry lookup_hash_entry(Agg *node, TupleTableSlot *slot);
-static TupleTableSlot *agg_retrieve_direct(Agg *node);
-static void agg_fill_hash_table(Agg *node);
-static TupleTableSlot *agg_retrieve_hash_table(Agg *node);
+static void build_hash_table(AggState *aggstate);
+static AggHashEntry lookup_hash_entry(AggState *aggstate,
+									  TupleTableSlot *slot);
+static TupleTableSlot *agg_retrieve_direct(AggState *aggstate);
+static void agg_fill_hash_table(AggState *aggstate);
+static TupleTableSlot *agg_retrieve_hash_table(AggState *aggstate);
 static Datum GetAggInitVal(Datum textInitVal, Oid transtype);
 
 
@@ -521,7 +522,7 @@ finalize_aggregate(AggState *aggstate,
 {
 	MemoryContext oldContext;
 
-	oldContext = MemoryContextSwitchTo(aggstate->csstate.cstate.cs_ExprContext->ecxt_per_tuple_memory);
+	oldContext = MemoryContextSwitchTo(aggstate->ss.ps.ps_ExprContext->ecxt_per_tuple_memory);
 
 	/*
 	 * Apply the agg's finalfn if one is provided, else return transValue.
@@ -572,9 +573,9 @@ finalize_aggregate(AggState *aggstate,
  * The hash table always lives in the aggcontext memory context.
  */
 static void
-build_hash_table(Agg *node)
+build_hash_table(AggState *aggstate)
 {
-	AggState   *aggstate = node->aggstate;
+	Agg			   *node = (Agg *) aggstate->ss.ps.plan;
 	AggHashTable	hashtable;
 	Size			tabsize;
 
@@ -596,9 +597,9 @@ build_hash_table(Agg *node)
  * When called, CurrentMemoryContext should be the per-query context.
  */
 static AggHashEntry
-lookup_hash_entry(Agg *node, TupleTableSlot *slot)
+lookup_hash_entry(AggState *aggstate, TupleTableSlot *slot)
 {
-	AggState   *aggstate = node->aggstate;
+	Agg		   *node = (Agg *) aggstate->ss.ps.plan;
 	AggHashTable hashtable = aggstate->hashtable;
 	MemoryContext	tmpmem = aggstate->tmpcontext->ecxt_per_tuple_memory;
 	HeapTuple	tuple = slot->val;
@@ -684,16 +685,14 @@ lookup_hash_entry(Agg *node, TupleTableSlot *slot)
  *	  the result tuple.
  */
 TupleTableSlot *
-ExecAgg(Agg *node)
+ExecAgg(AggState *node)
 {
-	AggState   *aggstate = node->aggstate;
-
-	if (aggstate->agg_done)
+	if (node->agg_done)
 		return NULL;
 
-	if (node->aggstrategy == AGG_HASHED)
+	if (((Agg *) node->ss.ps.plan)->aggstrategy == AGG_HASHED)
 	{
-		if (!aggstate->table_filled)
+		if (!node->table_filled)
 			agg_fill_hash_table(node);
 		return agg_retrieve_hash_table(node);
 	}
@@ -707,10 +706,10 @@ ExecAgg(Agg *node)
  * ExecAgg for non-hashed case
  */
 static TupleTableSlot *
-agg_retrieve_direct(Agg *node)
+agg_retrieve_direct(AggState *aggstate)
 {
-	AggState   *aggstate;
-	Plan	   *outerPlan;
+	Agg		   *node = (Agg *) aggstate->ss.ps.plan;
+	PlanState  *outerPlan;
 	ExprContext *econtext;
 	ExprContext *tmpcontext;
 	ProjectionInfo *projInfo;
@@ -726,22 +725,21 @@ agg_retrieve_direct(Agg *node)
 	/*
 	 * get state info from node
 	 */
-	aggstate = node->aggstate;
-	outerPlan = outerPlan(node);
+	outerPlan = outerPlanState(aggstate);
 	/* econtext is the per-output-tuple expression context */
-	econtext = aggstate->csstate.cstate.cs_ExprContext;
+	econtext = aggstate->ss.ps.ps_ExprContext;
 	aggvalues = econtext->ecxt_aggvalues;
 	aggnulls = econtext->ecxt_aggnulls;
 	/* tmpcontext is the per-input-tuple expression context */
 	tmpcontext = aggstate->tmpcontext;
-	projInfo = aggstate->csstate.cstate.cs_ProjInfo;
+	projInfo = aggstate->ss.ps.ps_ProjInfo;
 	peragg = aggstate->peragg;
 	pergroup = aggstate->pergroup;
-	firstSlot = aggstate->csstate.css_ScanTupleSlot;
+	firstSlot = aggstate->ss.ss_ScanTupleSlot;
 
 	/*
 	 * We loop retrieving groups until we find one matching
-	 * node->plan.qual
+	 * aggstate->ss.ps.qual
 	 */
 	do
 	{
@@ -754,7 +752,7 @@ agg_retrieve_direct(Agg *node)
 		 */
 		if (aggstate->grp_firstTuple == NULL)
 		{
-			outerslot = ExecProcNode(outerPlan, (Plan *) node);
+			outerslot = ExecProcNode(outerPlan);
 			if (!TupIsNull(outerslot))
 			{
 				/*
@@ -810,7 +808,7 @@ agg_retrieve_direct(Agg *node)
 				/* Reset per-input-tuple context after each tuple */
 				ResetExprContext(tmpcontext);
 
-				outerslot = ExecProcNode(outerPlan, (Plan *) node);
+				outerslot = ExecProcNode(outerPlan);
 				if (TupIsNull(outerslot))
 				{
 					/* no more outer-plan tuples available */
@@ -917,7 +915,7 @@ agg_retrieve_direct(Agg *node)
 		 * Otherwise, return the tuple.
 		 */
 	}
-	while (!ExecQual(node->plan.qual, econtext, false));
+	while (!ExecQual(aggstate->ss.ps.qual, econtext, false));
 
 	return resultSlot;
 }
@@ -926,10 +924,9 @@ agg_retrieve_direct(Agg *node)
  * ExecAgg for hashed case: phase 1, read input and build hash table
  */
 static void
-agg_fill_hash_table(Agg *node)
+agg_fill_hash_table(AggState *aggstate)
 {
-	AggState   *aggstate;
-	Plan	   *outerPlan;
+	PlanState  *outerPlan;
 	ExprContext *tmpcontext;
 	AggHashEntry	entry;
 	TupleTableSlot *outerslot;
@@ -937,8 +934,7 @@ agg_fill_hash_table(Agg *node)
 	/*
 	 * get state info from node
 	 */
-	aggstate = node->aggstate;
-	outerPlan = outerPlan(node);
+	outerPlan = outerPlanState(aggstate);
 	/* tmpcontext is the per-input-tuple expression context */
 	tmpcontext = aggstate->tmpcontext;
 
@@ -948,14 +944,14 @@ agg_fill_hash_table(Agg *node)
 	 */
 	for (;;)
 	{
-		outerslot = ExecProcNode(outerPlan, (Plan *) node);
+		outerslot = ExecProcNode(outerPlan);
 		if (TupIsNull(outerslot))
 			break;
 		/* set up for advance_aggregates call */
 		tmpcontext->ecxt_scantuple = outerslot;
 
 		/* Find or build hashtable entry for this tuple's group */
-		entry = lookup_hash_entry(node, outerslot);
+		entry = lookup_hash_entry(aggstate, outerslot);
 
 		/* Advance the aggregates */
 		advance_aggregates(aggstate, entry->pergroup);
@@ -974,9 +970,8 @@ agg_fill_hash_table(Agg *node)
  * ExecAgg for hashed case: phase 2, retrieving groups from hash table
  */
 static TupleTableSlot *
-agg_retrieve_hash_table(Agg *node)
+agg_retrieve_hash_table(AggState *aggstate)
 {
-	AggState   *aggstate;
 	ExprContext *econtext;
 	ProjectionInfo *projInfo;
 	Datum	   *aggvalues;
@@ -992,19 +987,18 @@ agg_retrieve_hash_table(Agg *node)
 	/*
 	 * get state info from node
 	 */
-	aggstate = node->aggstate;
 	/* econtext is the per-output-tuple expression context */
-	econtext = aggstate->csstate.cstate.cs_ExprContext;
+	econtext = aggstate->ss.ps.ps_ExprContext;
 	aggvalues = econtext->ecxt_aggvalues;
 	aggnulls = econtext->ecxt_aggnulls;
-	projInfo = aggstate->csstate.cstate.cs_ProjInfo;
+	projInfo = aggstate->ss.ps.ps_ProjInfo;
 	peragg = aggstate->peragg;
 	hashtable = aggstate->hashtable;
-	firstSlot = aggstate->csstate.css_ScanTupleSlot;
+	firstSlot = aggstate->ss.ss_ScanTupleSlot;
 
 	/*
-	 * We loop retrieving groups until we find one matching
-	 * node->plan.qual
+	 * We loop retrieving groups until we find one satisfying
+	 * aggstate->ss.ps.qual
 	 */
 	do
 	{
@@ -1071,7 +1065,7 @@ agg_retrieve_hash_table(Agg *node)
 		 * Otherwise, return the tuple.
 		 */
 	}
-	while (!ExecQual(node->plan.qual, econtext, false));
+	while (!ExecQual(aggstate->ss.ps.qual, econtext, false));
 
 	return resultSlot;
 }
@@ -1083,8 +1077,8 @@ agg_retrieve_hash_table(Agg *node)
  *	planner and initializes its outer subtree
  * -----------------
  */
-bool
-ExecInitAgg(Agg *node, EState *estate, Plan *parent)
+AggState *
+ExecInitAgg(Agg *node, EState *estate)
 {
 	AggState   *aggstate;
 	AggStatePerAgg peragg;
@@ -1094,16 +1088,15 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
 				aggno;
 	List	   *alist;
 
-	/*
-	 * assign the node's execution state
-	 */
-	node->plan.state = estate;
-
 	/*
 	 * create state structure
 	 */
 	aggstate = makeNode(AggState);
-	node->aggstate = aggstate;
+	aggstate->ss.ps.plan = (Plan *) node;
+	aggstate->ss.ps.state = estate;
+
+	aggstate->aggs = NIL;
+	aggstate->numaggs = 0;
 	aggstate->eqfunctions = NULL;
 	aggstate->peragg = NULL;
 	aggstate->agg_done = false;
@@ -1111,38 +1104,14 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
 	aggstate->grp_firstTuple = NULL;
 	aggstate->hashtable = NULL;
 
-	/*
-	 * find aggregates in targetlist and quals
-	 *
-	 * Note: pull_agg_clauses also checks that no aggs contain other agg
-	 * calls in their arguments.  This would make no sense under SQL
-	 * semantics anyway (and it's forbidden by the spec).  Because that is
-	 * true, we don't need to worry about evaluating the aggs in any
-	 * particular order.
-	 */
-	aggstate->aggs = nconc(pull_agg_clause((Node *) node->plan.targetlist),
-						   pull_agg_clause((Node *) node->plan.qual));
-	aggstate->numaggs = numaggs = length(aggstate->aggs);
-	if (numaggs <= 0)
-	{
-		/*
-		 * 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.
-		 */
-		numaggs = 1;
-	}
-
 	/*
 	 * Create expression contexts.  We need two, one for per-input-tuple
 	 * processing and one for per-output-tuple processing.  We cheat a little
 	 * by using ExecAssignExprContext() to build both.
 	 */
-	ExecAssignExprContext(estate, &aggstate->csstate.cstate);
-	aggstate->tmpcontext = aggstate->csstate.cstate.cs_ExprContext;
-	ExecAssignExprContext(estate, &aggstate->csstate.cstate);
+	ExecAssignExprContext(estate, &aggstate->ss.ps);
+	aggstate->tmpcontext = aggstate->ss.ps.ps_ExprContext;
+	ExecAssignExprContext(estate, &aggstate->ss.ps);
 
 	/*
 	 * We also need a long-lived memory context for holding hashtable
@@ -1163,14 +1132,64 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitScanTupleSlot(estate, &aggstate->csstate);
-	ExecInitResultTupleSlot(estate, &aggstate->csstate.cstate);
+	ExecInitScanTupleSlot(estate, &aggstate->ss);
+	ExecInitResultTupleSlot(estate, &aggstate->ss.ps);
+
+	/*
+	 * initialize child expressions
+	 *
+	 * Note: ExecInitExpr finds Aggrefs for us, and also checks that no aggs
+	 * contain other agg calls in their arguments.  This would make no sense
+	 * under SQL semantics anyway (and it's forbidden by the spec).  Because
+	 * that is true, we don't need to worry about evaluating the aggs in any
+	 * particular order.
+	 */
+	aggstate->ss.ps.targetlist = (List *)
+		ExecInitExpr((Node *) node->plan.targetlist,
+					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual = (List *)
+		ExecInitExpr((Node *) node->plan.qual,
+					 (PlanState *) aggstate);
+
+	/*
+	 * initialize child nodes
+	 */
+	outerPlan = outerPlan(node);
+	outerPlanState(aggstate) = ExecInitNode(outerPlan, estate);
+
+	/*
+	 * initialize source tuple type.
+	 */
+	ExecAssignScanTypeFromOuterPlan(&aggstate->ss);
+
+	/*
+	 * Initialize result tuple type and projection info.
+	 */
+	ExecAssignResultTypeFromTL(&aggstate->ss.ps);
+	ExecAssignProjectionInfo(&aggstate->ss.ps);
+
+	/*
+	 * get the count of aggregates in targetlist and quals
+	 */
+	numaggs = aggstate->numaggs;
+	Assert(numaggs == length(aggstate->aggs));
+	if (numaggs <= 0)
+	{
+		/*
+		 * 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.
+		 */
+		numaggs = 1;
+	}
 
 	/*
 	 * Set up aggregate-result storage in the output expr context, and also
 	 * allocate my private per-agg working storage
 	 */
-	econtext = aggstate->csstate.cstate.cs_ExprContext;
+	econtext = aggstate->ss.ps.ps_ExprContext;
 	econtext->ecxt_aggvalues = (Datum *) palloc0(sizeof(Datum) * numaggs);
 	econtext->ecxt_aggnulls = (bool *) palloc0(sizeof(bool) * numaggs);
 
@@ -1179,7 +1198,7 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
 
 	if (node->aggstrategy == AGG_HASHED)
 	{
-		build_hash_table(node);
+		build_hash_table(aggstate);
 		aggstate->table_filled = false;
 	}
 	else
@@ -1190,30 +1209,13 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
 		aggstate->pergroup = pergroup;
 	}
 
-	/*
-	 * initialize child nodes
-	 */
-	outerPlan = outerPlan(node);
-	ExecInitNode(outerPlan, estate, (Plan *) node);
-
-	/*
-	 * initialize source tuple type.
-	 */
-	ExecAssignScanTypeFromOuterPlan((Plan *) node, &aggstate->csstate);
-
-	/*
-	 * Initialize result tuple type and projection info.
-	 */
-	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),
+			execTuplesMatchPrepare(ExecGetScanType(&aggstate->ss),
 								   node->numCols,
 								   node->grpColIdx);
 	}
@@ -1330,7 +1332,7 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
 		ReleaseSysCache(aggTuple);
 	}
 
-	return TRUE;
+	return aggstate;
 }
 
 static Datum
@@ -1372,84 +1374,82 @@ ExecCountSlotsAgg(Agg *node)
 }
 
 void
-ExecEndAgg(Agg *node)
+ExecEndAgg(AggState *node)
 {
-	AggState   *aggstate = node->aggstate;
-	Plan	   *outerPlan;
+	PlanState  *outerPlan;
 	int			aggno;
 
 	/* Make sure we have closed any open tuplesorts */
-	for (aggno = 0; aggno < aggstate->numaggs; aggno++)
+	for (aggno = 0; aggno < node->numaggs; aggno++)
 	{
-		AggStatePerAgg peraggstate = &aggstate->peragg[aggno];
+		AggStatePerAgg peraggstate = &node->peragg[aggno];
 
 		if (peraggstate->sortstate)
 			tuplesort_end(peraggstate->sortstate);
 	}
 
-	ExecFreeProjectionInfo(&aggstate->csstate.cstate);
+	ExecFreeProjectionInfo(&node->ss.ps);
 
 	/*
 	 * Free both the expr contexts.
 	 */
-	ExecFreeExprContext(&aggstate->csstate.cstate);
-	aggstate->csstate.cstate.cs_ExprContext = aggstate->tmpcontext;
-	ExecFreeExprContext(&aggstate->csstate.cstate);
+	ExecFreeExprContext(&node->ss.ps);
+	node->ss.ps.ps_ExprContext = node->tmpcontext;
+	ExecFreeExprContext(&node->ss.ps);
 
-	MemoryContextDelete(aggstate->aggcontext);
+	MemoryContextDelete(node->aggcontext);
 
-	outerPlan = outerPlan(node);
-	ExecEndNode(outerPlan, (Plan *) node);
+	outerPlan = outerPlanState(node);
+	ExecEndNode(outerPlan);
 
 	/* clean up tuple table */
-	ExecClearTuple(aggstate->csstate.css_ScanTupleSlot);
-	if (aggstate->grp_firstTuple != NULL)
+	ExecClearTuple(node->ss.ss_ScanTupleSlot);
+	if (node->grp_firstTuple != NULL)
 	{
-		heap_freetuple(aggstate->grp_firstTuple);
-		aggstate->grp_firstTuple = NULL;
+		heap_freetuple(node->grp_firstTuple);
+		node->grp_firstTuple = NULL;
 	}
 }
 
 void
-ExecReScanAgg(Agg *node, ExprContext *exprCtxt, Plan *parent)
+ExecReScanAgg(AggState *node, ExprContext *exprCtxt)
 {
-	AggState   *aggstate = node->aggstate;
-	ExprContext *econtext = aggstate->csstate.cstate.cs_ExprContext;
+	ExprContext *econtext = node->ss.ps.ps_ExprContext;
 	int			aggno;
 
 	/* Make sure we have closed any open tuplesorts */
-	for (aggno = 0; aggno < aggstate->numaggs; aggno++)
+	for (aggno = 0; aggno < node->numaggs; aggno++)
 	{
-		AggStatePerAgg peraggstate = &aggstate->peragg[aggno];
+		AggStatePerAgg peraggstate = &node->peragg[aggno];
 
 		if (peraggstate->sortstate)
 			tuplesort_end(peraggstate->sortstate);
 		peraggstate->sortstate = NULL;
 	}
 
-	aggstate->agg_done = false;
-	if (aggstate->grp_firstTuple != NULL)
+	node->agg_done = false;
+	if (node->grp_firstTuple != NULL)
 	{
-		heap_freetuple(aggstate->grp_firstTuple);
-		aggstate->grp_firstTuple = NULL;
+		heap_freetuple(node->grp_firstTuple);
+		node->grp_firstTuple = NULL;
 	}
-	MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * aggstate->numaggs);
-	MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * aggstate->numaggs);
+	MemSet(econtext->ecxt_aggvalues, 0, sizeof(Datum) * node->numaggs);
+	MemSet(econtext->ecxt_aggnulls, 0, sizeof(bool) * node->numaggs);
 
-	MemoryContextReset(aggstate->aggcontext);
+	MemoryContextReset(node->aggcontext);
 
-	if (node->aggstrategy == AGG_HASHED)
+	if (((Agg *) node->ss.ps.plan)->aggstrategy == AGG_HASHED)
 	{
 		build_hash_table(node);
-		aggstate->table_filled = false;
+		node->table_filled = false;
 	}
 
 	/*
 	 * if chgParam of subnode is not null then plan will be re-scanned by
 	 * first ExecProcNode.
 	 */
-	if (((Plan *) node)->lefttree->chgParam == NULL)
-		ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
+	if (((PlanState *) node)->lefttree->chgParam == NIL)
+		ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
 }
 
 /*
diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c
index 9d0e5642229..bcb50fb7979 100644
--- a/src/backend/executor/nodeAppend.c
+++ b/src/backend/executor/nodeAppend.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.50 2002/11/13 00:39:47 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.51 2002/12/05 15:50:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -62,30 +62,27 @@
 #include "executor/nodeAppend.h"
 #include "parser/parsetree.h"
 
-static bool exec_append_initialize_next(Append *node);
+static bool exec_append_initialize_next(AppendState *appendstate);
 
 
 /* ----------------------------------------------------------------
  *		exec_append_initialize_next
  *
- *		Sets up the append node state (i.e. the append state node)
- *		for the "next" scan.
+ *		Sets up the append state node for the "next" scan.
  *
  *		Returns t iff there is a "next" scan to process.
  * ----------------------------------------------------------------
  */
 static bool
-exec_append_initialize_next(Append *node)
+exec_append_initialize_next(AppendState *appendstate)
 {
 	EState	   *estate;
-	AppendState *appendstate;
 	int			whichplan;
 
 	/*
 	 * get information from the append node
 	 */
-	estate = node->plan.state;
-	appendstate = node->appendstate;
+	estate = appendstate->ps.state;
 	whichplan = appendstate->as_whichplan;
 
 	if (whichplan < appendstate->as_firstplan)
@@ -116,7 +113,7 @@ exec_append_initialize_next(Append *node)
 		 * If we are controlling the target relation, select the proper
 		 * active ResultRelInfo and junk filter for this target.
 		 */
-		if (node->isTarget)
+		if (((Append *) appendstate->ps.plan)->isTarget)
 		{
 			Assert(whichplan < estate->es_num_result_relations);
 			estate->es_result_relation_info =
@@ -132,9 +129,7 @@ exec_append_initialize_next(Append *node)
 /* ----------------------------------------------------------------
  *		ExecInitAppend
  *
- *		Begins all of the subscans of the append node, storing the
- *		scan structures in the 'initialized' vector of the append-state
- *		structure.
+ *		Begin all of the subscans of the append node.
  *
  *	   (This is potentially wasteful, since the entire result of the
  *		append node may not be scanned, but this way all of the
@@ -146,36 +141,31 @@ exec_append_initialize_next(Append *node)
  *		subplan that corresponds to the target relation being checked.
  * ----------------------------------------------------------------
  */
-bool
-ExecInitAppend(Append *node, EState *estate, Plan *parent)
+AppendState *
+ExecInitAppend(Append *node, EState *estate)
 {
-	AppendState *appendstate;
+	AppendState *appendstate = makeNode(AppendState);
+	PlanState **appendplanstates;
 	int			nplans;
-	List	   *appendplans;
-	bool	   *initialized;
 	int			i;
 	Plan	   *initNode;
 
 	CXT1_printf("ExecInitAppend: context is %d\n", CurrentMemoryContext);
 
 	/*
-	 * assign execution state to node and get information for append state
+	 * Set up empty vector of subplan states
 	 */
-	node->plan.state = estate;
+	nplans = length(node->appendplans);
 
-	appendplans = node->appendplans;
-	nplans = length(appendplans);
-
-	initialized = (bool *) palloc0(nplans * sizeof(bool));
+	appendplanstates = (PlanState **) palloc0(nplans * sizeof(PlanState *));
 
 	/*
 	 * create new AppendState for our append node
 	 */
-	appendstate = makeNode(AppendState);
+	appendstate->ps.plan = (Plan *) node;
+	appendstate->ps.state = estate;
+	appendstate->appendplans = appendplanstates;
 	appendstate->as_nplans = nplans;
-	appendstate->as_initialized = initialized;
-
-	node->appendstate = appendstate;
 
 	/*
 	 * Do we want to scan just one subplan?  (Special case for
@@ -212,36 +202,36 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent)
 	 * append nodes still have Result slots, which hold pointers to
 	 * tuples, so we have to initialize them.
 	 */
-	ExecInitResultTupleSlot(estate, &appendstate->cstate);
+	ExecInitResultTupleSlot(estate, &appendstate->ps);
 
 	/*
 	 * call ExecInitNode on each of the plans to be executed and save the
-	 * results into the array "initialized".  Note we *must* set
+	 * results into the array "appendplans".  Note we *must* set
 	 * estate->es_result_relation_info correctly while we initialize each
 	 * sub-plan; ExecAssignResultTypeFromTL depends on that!
 	 */
 	for (i = appendstate->as_firstplan; i <= appendstate->as_lastplan; i++)
 	{
 		appendstate->as_whichplan = i;
-		exec_append_initialize_next(node);
+		exec_append_initialize_next(appendstate);
 
-		initNode = (Plan *) nth(i, appendplans);
-		initialized[i] = ExecInitNode(initNode, estate, (Plan *) node);
+		initNode = (Plan *) nth(i, node->appendplans);
+		appendplanstates[i] = ExecInitNode(initNode, estate);
 	}
 
 	/*
 	 * initialize tuple type
 	 */
-	ExecAssignResultTypeFromTL((Plan *) node, &appendstate->cstate);
-	appendstate->cstate.cs_ProjInfo = NULL;
+	ExecAssignResultTypeFromTL(&appendstate->ps);
+	appendstate->ps.ps_ProjInfo = NULL;
 
 	/*
 	 * return the result from the first subplan's initialization
 	 */
 	appendstate->as_whichplan = appendstate->as_firstplan;
-	exec_append_initialize_next(node);
+	exec_append_initialize_next(appendstate);
 
-	return TRUE;
+	return appendstate;
 }
 
 int
@@ -264,13 +254,11 @@ ExecCountSlotsAppend(Append *node)
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
-ExecProcAppend(Append *node)
+ExecProcAppend(AppendState *node)
 {
 	EState	   *estate;
-	AppendState *appendstate;
 	int			whichplan;
-	List	   *appendplans;
-	Plan	   *subnode;
+	PlanState  *subnode;
 	TupleTableSlot *result;
 	TupleTableSlot *result_slot;
 	ScanDirection direction;
@@ -278,25 +266,20 @@ ExecProcAppend(Append *node)
 	/*
 	 * get information from the node
 	 */
-	appendstate = node->appendstate;
-	estate = node->plan.state;
+	estate = node->ps.state;
 	direction = estate->es_direction;
-	appendplans = node->appendplans;
-	whichplan = appendstate->as_whichplan;
-	result_slot = appendstate->cstate.cs_ResultTupleSlot;
+	whichplan = node->as_whichplan;
+	result_slot = node->ps.ps_ResultTupleSlot;
 
 	/*
 	 * figure out which subplan we are currently processing
 	 */
-	subnode = (Plan *) nth(whichplan, appendplans);
-
-	if (subnode == NULL)
-		elog(DEBUG1, "ExecProcAppend: subnode is NULL");
+	subnode = node->appendplans[whichplan];
 
 	/*
 	 * get a tuple from the subplan
 	 */
-	result = ExecProcNode(subnode, (Plan *) node);
+	result = ExecProcNode(subnode);
 
 	if (!TupIsNull(result))
 	{
@@ -316,9 +299,9 @@ ExecProcAppend(Append *node)
 		 * try processing again (recursively)
 		 */
 		if (ScanDirectionIsForward(direction))
-			appendstate->as_whichplan++;
+			node->as_whichplan++;
 		else
-			appendstate->as_whichplan--;
+			node->as_whichplan--;
 
 		/*
 		 * return something from next node or an empty slot if all of our
@@ -343,65 +326,56 @@ ExecProcAppend(Append *node)
  * ----------------------------------------------------------------
  */
 void
-ExecEndAppend(Append *node)
+ExecEndAppend(AppendState *node)
 {
-	EState	   *estate;
-	AppendState *appendstate;
+	PlanState **appendplans;
 	int			nplans;
-	List	   *appendplans;
-	bool	   *initialized;
 	int			i;
 
 	/*
 	 * get information from the node
 	 */
-	appendstate = node->appendstate;
-	estate = node->plan.state;
 	appendplans = node->appendplans;
-	nplans = appendstate->as_nplans;
-	initialized = appendstate->as_initialized;
+	nplans = node->as_nplans;
 
 	/*
-	 * shut down each of the subscans
+	 * shut down each of the subscans (that we've initialized)
 	 */
 	for (i = 0; i < nplans; i++)
 	{
-		if (initialized[i])
-			ExecEndNode((Plan *) nth(i, appendplans), (Plan *) node);
+		if (appendplans[i])
+			ExecEndNode(appendplans[i]);
 	}
 }
 
 void
-ExecReScanAppend(Append *node, ExprContext *exprCtxt, Plan *parent)
+ExecReScanAppend(AppendState *node, ExprContext *exprCtxt)
 {
-	AppendState *appendstate = node->appendstate;
 	int			i;
 
-	for (i = appendstate->as_firstplan; i <= appendstate->as_lastplan; i++)
+	for (i = node->as_firstplan; i <= node->as_lastplan; i++)
 	{
-		Plan	   *subnode;
-
-		subnode = (Plan *) nth(i, node->appendplans);
+		PlanState *subnode = node->appendplans[i];
 
 		/*
 		 * ExecReScan doesn't know about my subplans, so I have to do
 		 * changed-parameter signaling myself.
 		 */
-		if (node->plan.chgParam != NULL)
-			SetChangedParamList(subnode, node->plan.chgParam);
+		if (node->ps.chgParam != NIL)
+			SetChangedParamList(subnode, node->ps.chgParam);
 
 		/*
 		 * if chgParam of subnode is not null then plan will be re-scanned
 		 * by first ExecProcNode.
 		 */
-		if (subnode->chgParam == NULL)
+		if (subnode->chgParam == NIL)
 		{
 			/* make sure estate is correct for this subnode (needed??) */
-			appendstate->as_whichplan = i;
+			node->as_whichplan = i;
 			exec_append_initialize_next(node);
-			ExecReScan(subnode, exprCtxt, (Plan *) node);
+			ExecReScan(subnode, exprCtxt);
 		}
 	}
-	appendstate->as_whichplan = appendstate->as_firstplan;
+	node->as_whichplan = node->as_firstplan;
 	exec_append_initialize_next(node);
 }
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index 97da70b2b78..f6df416d0f9 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.13 2002/12/01 20:27:32 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.14 2002/12/05 15:50:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -34,7 +34,7 @@
 #include "utils/lsyscache.h"
 
 
-static TupleTableSlot *FunctionNext(FunctionScan *node);
+static TupleTableSlot *FunctionNext(FunctionScanState *node);
 static bool tupledesc_mismatch(TupleDesc tupdesc1, TupleDesc tupdesc2);
 
 /* ----------------------------------------------------------------
@@ -48,24 +48,22 @@ static bool tupledesc_mismatch(TupleDesc tupdesc1, TupleDesc tupdesc2);
  * ----------------------------------------------------------------
  */
 static TupleTableSlot *
-FunctionNext(FunctionScan *node)
+FunctionNext(FunctionScanState *node)
 {
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ScanDirection direction;
 	Tuplestorestate *tuplestorestate;
-	FunctionScanState *scanstate;
 	bool		should_free;
 	HeapTuple	heapTuple;
 
 	/*
 	 * get information from the estate and scan state
 	 */
-	scanstate = (FunctionScanState *) node->scan.scanstate;
-	estate = node->scan.plan.state;
+	estate = node->ss.ps.state;
 	direction = estate->es_direction;
 
-	tuplestorestate = scanstate->tuplestorestate;
+	tuplestorestate = node->tuplestorestate;
 
 	/*
 	 * If first time through, read all tuples from function and put them
@@ -74,13 +72,13 @@ FunctionNext(FunctionScan *node)
 	 */
 	if (tuplestorestate == NULL)
 	{
-		ExprContext *econtext = scanstate->csstate.cstate.cs_ExprContext;
+		ExprContext *econtext = node->ss.ps.ps_ExprContext;
 		TupleDesc	funcTupdesc;
 
-		scanstate->tuplestorestate = tuplestorestate =
-			ExecMakeTableFunctionResult(scanstate->funcexpr,
+		node->tuplestorestate = tuplestorestate =
+			ExecMakeTableFunctionResult(node->funcexpr,
 										econtext,
-										scanstate->tupdesc,
+										node->tupdesc,
 										&funcTupdesc);
 
 		/*
@@ -89,14 +87,14 @@ FunctionNext(FunctionScan *node)
 		 * well do it always.
 		 */
 		if (funcTupdesc &&
-			tupledesc_mismatch(scanstate->tupdesc, funcTupdesc))
+			tupledesc_mismatch(node->tupdesc, funcTupdesc))
 			elog(ERROR, "Query-specified return tuple and actual function return tuple do not match");
 	}
 
 	/*
 	 * Get the next tuple from tuplestore. Return NULL if no more tuples.
 	 */
-	slot = scanstate->csstate.css_ScanTupleSlot;
+	slot = node->ss.ss_ScanTupleSlot;
 	if (tuplestorestate)
 		heapTuple = tuplestore_getheaptuple(tuplestorestate,
 									   ScanDirectionIsForward(direction),
@@ -121,20 +119,20 @@ FunctionNext(FunctionScan *node)
  */
 
 TupleTableSlot *
-ExecFunctionScan(FunctionScan *node)
+ExecFunctionScan(FunctionScanState *node)
 {
 	/*
 	 * use FunctionNext as access method
 	 */
-	return ExecScan(&node->scan, (ExecScanAccessMtd) FunctionNext);
+	return ExecScan(&node->ss, (ExecScanAccessMtd) FunctionNext);
 }
 
 /* ----------------------------------------------------------------
  *		ExecInitFunctionScan
  * ----------------------------------------------------------------
  */
-bool
-ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
+FunctionScanState *
+ExecInitFunctionScan(FunctionScan *node, EState *estate)
 {
 	FunctionScanState *scanstate;
 	RangeTblEntry *rte;
@@ -145,34 +143,40 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
 	/*
 	 * FunctionScan should not have any children.
 	 */
-	Assert(outerPlan((Plan *) node) == NULL);
-	Assert(innerPlan((Plan *) node) == NULL);
-
-	/*
-	 * assign the node's execution state
-	 */
-	node->scan.plan.state = estate;
+	Assert(outerPlan(node) == NULL);
+	Assert(innerPlan(node) == NULL);
 
 	/*
 	 * create new ScanState for node
 	 */
 	scanstate = makeNode(FunctionScanState);
-	node->scan.scanstate = &scanstate->csstate;
+	scanstate->ss.ps.plan = (Plan *) node;
+	scanstate->ss.ps.state = estate;
 
 	/*
 	 * Miscellaneous initialization
 	 *
 	 * create expression context for node
 	 */
-	ExecAssignExprContext(estate, &scanstate->csstate.cstate);
+	ExecAssignExprContext(estate, &scanstate->ss.ps);
 
 #define FUNCTIONSCAN_NSLOTS 2
 
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &scanstate->csstate.cstate);
-	ExecInitScanTupleSlot(estate, &scanstate->csstate);
+	ExecInitResultTupleSlot(estate, &scanstate->ss.ps);
+	ExecInitScanTupleSlot(estate, &scanstate->ss);
+
+	/*
+	 * initialize child expressions
+	 */
+	scanstate->ss.ps.targetlist = (List *)
+		ExecInitExpr((Node *) node->scan.plan.targetlist,
+					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual = (List *)
+		ExecInitExpr((Node *) node->scan.plan.qual,
+					 (PlanState *) scanstate);
 
 	/*
 	 * get info about function
@@ -230,7 +234,7 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
 		elog(ERROR, "Unknown kind of return type specified for function");
 
 	scanstate->tupdesc = tupdesc;
-	ExecSetSlotDescriptor(scanstate->csstate.css_ScanTupleSlot,
+	ExecSetSlotDescriptor(scanstate->ss.ss_ScanTupleSlot,
 						  tupdesc, false);
 
 	/*
@@ -239,15 +243,15 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent)
 	scanstate->tuplestorestate = NULL;
 	scanstate->funcexpr = rte->funcexpr;
 
-	scanstate->csstate.cstate.cs_TupFromTlist = false;
+	scanstate->ss.ps.ps_TupFromTlist = false;
 
 	/*
 	 * initialize tuple type
 	 */
-	ExecAssignResultTypeFromTL((Plan *) node, &scanstate->csstate.cstate);
-	ExecAssignProjectionInfo((Plan *) node, &scanstate->csstate.cstate);
+	ExecAssignResultTypeFromTL(&scanstate->ss.ps);
+	ExecAssignProjectionInfo(&scanstate->ss.ps);
 
-	return TRUE;
+	return scanstate;
 }
 
 int
@@ -265,39 +269,26 @@ ExecCountSlotsFunctionScan(FunctionScan *node)
  * ----------------------------------------------------------------
  */
 void
-ExecEndFunctionScan(FunctionScan *node)
+ExecEndFunctionScan(FunctionScanState *node)
 {
-	FunctionScanState *scanstate;
-	EState	   *estate;
-
-	/*
-	 * get information from node
-	 */
-	scanstate = (FunctionScanState *) node->scan.scanstate;
-	estate = node->scan.plan.state;
-
 	/*
 	 * Free the projection info and the scan attribute info
-	 *
-	 * Note: we don't ExecFreeResultType(scanstate) because the rule manager
-	 * depends on the tupType returned by ExecMain().  So for now, this is
-	 * freed at end-transaction time.  -cim 6/2/91
 	 */
-	ExecFreeProjectionInfo(&scanstate->csstate.cstate);
-	ExecFreeExprContext(&scanstate->csstate.cstate);
+	ExecFreeProjectionInfo(&node->ss.ps);
+	ExecFreeExprContext(&node->ss.ps);
 
 	/*
 	 * clean out the tuple table
 	 */
-	ExecClearTuple(scanstate->csstate.cstate.cs_ResultTupleSlot);
-	ExecClearTuple(scanstate->csstate.css_ScanTupleSlot);
+	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+	ExecClearTuple(node->ss.ss_ScanTupleSlot);
 
 	/*
 	 * Release tuplestore resources
 	 */
-	if (scanstate->tuplestorestate != NULL)
-		tuplestore_end(scanstate->tuplestorestate);
-	scanstate->tuplestorestate = NULL;
+	if (node->tuplestorestate != NULL)
+		tuplestore_end(node->tuplestorestate);
+	node->tuplestorestate = NULL;
 }
 
 /* ----------------------------------------------------------------
@@ -307,19 +298,15 @@ ExecEndFunctionScan(FunctionScan *node)
  * ----------------------------------------------------------------
  */
 void
-ExecFunctionMarkPos(FunctionScan *node)
+ExecFunctionMarkPos(FunctionScanState *node)
 {
-	FunctionScanState *scanstate;
-
-	scanstate = (FunctionScanState *) node->scan.scanstate;
-
 	/*
 	 * if we haven't materialized yet, just return.
 	 */
-	if (!scanstate->tuplestorestate)
+	if (!node->tuplestorestate)
 		return;
 
-	tuplestore_markpos(scanstate->tuplestorestate);
+	tuplestore_markpos(node->tuplestorestate);
 }
 
 /* ----------------------------------------------------------------
@@ -329,19 +316,15 @@ ExecFunctionMarkPos(FunctionScan *node)
  * ----------------------------------------------------------------
  */
 void
-ExecFunctionRestrPos(FunctionScan *node)
+ExecFunctionRestrPos(FunctionScanState *node)
 {
-	FunctionScanState *scanstate;
-
-	scanstate = (FunctionScanState *) node->scan.scanstate;
-
 	/*
 	 * if we haven't materialized yet, just return.
 	 */
-	if (!scanstate->tuplestorestate)
+	if (!node->tuplestorestate)
 		return;
 
-	tuplestore_restorepos(scanstate->tuplestorestate);
+	tuplestore_restorepos(node->tuplestorestate);
 }
 
 /* ----------------------------------------------------------------
@@ -351,21 +334,14 @@ ExecFunctionRestrPos(FunctionScan *node)
  * ----------------------------------------------------------------
  */
 void
-ExecFunctionReScan(FunctionScan *node, ExprContext *exprCtxt, Plan *parent)
+ExecFunctionReScan(FunctionScanState *node, ExprContext *exprCtxt)
 {
-	FunctionScanState *scanstate;
-
-	/*
-	 * get information from node
-	 */
-	scanstate = (FunctionScanState *) node->scan.scanstate;
-
-	ExecClearTuple(scanstate->csstate.cstate.cs_ResultTupleSlot);
+	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
 
 	/*
 	 * If we haven't materialized yet, just return.
 	 */
-	if (!scanstate->tuplestorestate)
+	if (!node->tuplestorestate)
 		return;
 
 	/*
@@ -374,13 +350,13 @@ ExecFunctionReScan(FunctionScan *node, ExprContext *exprCtxt, Plan *parent)
 	 * whether the function expression contains parameters and/or is
 	 * marked volatile.  FIXME soon.
 	 */
-	if (node->scan.plan.chgParam != NULL)
+	if (node->ss.ps.chgParam != NULL)
 	{
-		tuplestore_end(scanstate->tuplestorestate);
-		scanstate->tuplestorestate = NULL;
+		tuplestore_end(node->tuplestorestate);
+		node->tuplestorestate = NULL;
 	}
 	else
-		tuplestore_rescan(scanstate->tuplestorestate);
+		tuplestore_rescan(node->tuplestorestate);
 }
 
 
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index d41bcbb6fcc..3d562a476b8 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.50 2002/11/29 21:39:11 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.51 2002/12/05 15:50:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -38,12 +38,13 @@
  *		Return one tuple for each group of matching input tuples.
  */
 TupleTableSlot *
-ExecGroup(Group *node)
+ExecGroup(GroupState *node)
 {
-	GroupState *grpstate;
 	EState	   *estate;
 	ExprContext *econtext;
 	TupleDesc	tupdesc;
+	int			numCols;
+	AttrNumber *grpColIdx;
 	HeapTuple	outerTuple = NULL;
 	HeapTuple	firsttuple;
 	TupleTableSlot *outerslot;
@@ -53,12 +54,13 @@ ExecGroup(Group *node)
 	/*
 	 * get state info from node
 	 */
-	grpstate = node->grpstate;
-	if (grpstate->grp_done)
+	if (node->grp_done)
 		return NULL;
-	estate = node->plan.state;
-	econtext = node->grpstate->csstate.cstate.cs_ExprContext;
-	tupdesc = ExecGetScanType(&grpstate->csstate);
+	estate = node->ss.ps.state;
+	econtext = node->ss.ps.ps_ExprContext;
+	tupdesc = ExecGetScanType(&node->ss);
+	numCols = ((Group *) node->ss.ps.plan)->numCols;
+	grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx;
 
 	/*
 	 * We need not call ResetExprContext here because execTuplesMatch will
@@ -67,16 +69,16 @@ ExecGroup(Group *node)
 
 	/* If we don't already have first tuple of group, fetch it */
 	/* this should occur on the first call only */
-	firsttuple = grpstate->grp_firstTuple;
+	firsttuple = node->grp_firstTuple;
 	if (firsttuple == NULL)
 	{
-		outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
+		outerslot = ExecProcNode(outerPlanState(node));
 		if (TupIsNull(outerslot))
 		{
-			grpstate->grp_done = TRUE;
+			node->grp_done = TRUE;
 			return NULL;
 		}
-		grpstate->grp_firstTuple = firsttuple =
+		node->grp_firstTuple = firsttuple =
 			heap_copytuple(outerslot->val);
 	}
 
@@ -85,10 +87,10 @@ ExecGroup(Group *node)
 	 */
 	for (;;)
 	{
-		outerslot = ExecProcNode(outerPlan(node), (Plan *) node);
+		outerslot = ExecProcNode(outerPlanState(node));
 		if (TupIsNull(outerslot))
 		{
-			grpstate->grp_done = TRUE;
+			node->grp_done = TRUE;
 			outerTuple = NULL;
 			break;
 		}
@@ -100,8 +102,8 @@ ExecGroup(Group *node)
 		 */
 		if (!execTuplesMatch(firsttuple, outerTuple,
 							 tupdesc,
-							 node->numCols, node->grpColIdx,
-							 grpstate->eqfunctions,
+							 numCols, grpColIdx,
+							 node->eqfunctions,
 							 econtext->ecxt_per_tuple_memory))
 			break;
 	}
@@ -111,18 +113,18 @@ ExecGroup(Group *node)
 	 * group, and store it in the result tuple slot.
 	 */
 	ExecStoreTuple(firsttuple,
-				   grpstate->csstate.css_ScanTupleSlot,
+				   node->ss.ss_ScanTupleSlot,
 				   InvalidBuffer,
 				   false);
-	econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot;
-	projInfo = grpstate->csstate.cstate.cs_ProjInfo;
+	econtext->ecxt_scantuple = node->ss.ss_ScanTupleSlot;
+	projInfo = node->ss.ps.ps_ProjInfo;
 	resultSlot = ExecProject(projInfo, NULL);
 
 	/* save first tuple of next group, if we are not done yet */
-	if (!grpstate->grp_done)
+	if (!node->grp_done)
 	{
 		heap_freetuple(firsttuple);
-		grpstate->grp_firstTuple = heap_copytuple(outerTuple);
+		node->grp_firstTuple = heap_copytuple(outerTuple);
 	}
 
 	return resultSlot;
@@ -135,65 +137,69 @@ ExecGroup(Group *node)
  *	planner and initializes its outer subtree
  * -----------------
  */
-bool
-ExecInitGroup(Group *node, EState *estate, Plan *parent)
+GroupState *
+ExecInitGroup(Group *node, EState *estate)
 {
 	GroupState *grpstate;
-	Plan	   *outerPlan;
-
-	/*
-	 * assign the node's execution state
-	 */
-	node->plan.state = estate;
 
 	/*
 	 * create state structure
 	 */
 	grpstate = makeNode(GroupState);
-	node->grpstate = grpstate;
+	grpstate->ss.ps.plan = (Plan *) node;
+	grpstate->ss.ps.state = estate;
 	grpstate->grp_firstTuple = NULL;
 	grpstate->grp_done = FALSE;
 
 	/*
 	 * create expression context
 	 */
-	ExecAssignExprContext(estate, &grpstate->csstate.cstate);
+	ExecAssignExprContext(estate, &grpstate->ss.ps);
 
 #define GROUP_NSLOTS 2
 
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitScanTupleSlot(estate, &grpstate->csstate);
-	ExecInitResultTupleSlot(estate, &grpstate->csstate.cstate);
+	ExecInitScanTupleSlot(estate, &grpstate->ss);
+	ExecInitResultTupleSlot(estate, &grpstate->ss.ps);
+
+	/*
+	 * initialize child expressions
+	 */
+	grpstate->ss.ps.targetlist = (List *)
+		ExecInitExpr((Node *) node->plan.targetlist,
+					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual = (List *)
+		ExecInitExpr((Node *) node->plan.qual,
+					 (PlanState *) grpstate);
 
 	/*
-	 * initializes child nodes
+	 * initialize child nodes
 	 */
-	outerPlan = outerPlan(node);
-	ExecInitNode(outerPlan, estate, (Plan *) node);
+	outerPlanState(grpstate) = ExecInitNode(outerPlan(node), estate);
 
 	/*
 	 * initialize tuple type.
 	 */
-	ExecAssignScanTypeFromOuterPlan((Plan *) node, &grpstate->csstate);
+	ExecAssignScanTypeFromOuterPlan(&grpstate->ss);
 
 	/*
 	 * Initialize tuple type for both result and scan. This node does no
 	 * projection
 	 */
-	ExecAssignResultTypeFromTL((Plan *) node, &grpstate->csstate.cstate);
-	ExecAssignProjectionInfo((Plan *) node, &grpstate->csstate.cstate);
+	ExecAssignResultTypeFromTL(&grpstate->ss.ps);
+	ExecAssignProjectionInfo(&grpstate->ss.ps);
 
 	/*
 	 * Precompute fmgr lookup data for inner loop
 	 */
 	grpstate->eqfunctions =
-		execTuplesMatchPrepare(ExecGetScanType(&grpstate->csstate),
+		execTuplesMatchPrepare(ExecGetScanType(&grpstate->ss),
 							   node->numCols,
 							   node->grpColIdx);
 
-	return TRUE;
+	return grpstate;
 }
 
 int
@@ -208,43 +214,38 @@ ExecCountSlotsGroup(Group *node)
  * -----------------------
  */
 void
-ExecEndGroup(Group *node)
+ExecEndGroup(GroupState *node)
 {
-	GroupState *grpstate;
-	Plan	   *outerPlan;
+	PlanState  *outerPlan;
 
-	grpstate = node->grpstate;
+	ExecFreeProjectionInfo(&node->ss.ps);
+	ExecFreeExprContext(&node->ss.ps);
 
-	ExecFreeProjectionInfo(&grpstate->csstate.cstate);
-	ExecFreeExprContext(&grpstate->csstate.cstate);
-
-	outerPlan = outerPlan(node);
-	ExecEndNode(outerPlan, (Plan *) node);
+	outerPlan = outerPlanState(node);
+	ExecEndNode(outerPlan);
 
 	/* clean up tuple table */
-	ExecClearTuple(grpstate->csstate.css_ScanTupleSlot);
-	if (grpstate->grp_firstTuple != NULL)
+	ExecClearTuple(node->ss.ss_ScanTupleSlot);
+	if (node->grp_firstTuple != NULL)
 	{
-		heap_freetuple(grpstate->grp_firstTuple);
-		grpstate->grp_firstTuple = NULL;
+		heap_freetuple(node->grp_firstTuple);
+		node->grp_firstTuple = NULL;
 	}
 }
 
 void
-ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
+ExecReScanGroup(GroupState *node, ExprContext *exprCtxt)
 {
-	GroupState *grpstate = node->grpstate;
-
-	grpstate->grp_done = FALSE;
-	if (grpstate->grp_firstTuple != NULL)
+	node->grp_done = FALSE;
+	if (node->grp_firstTuple != NULL)
 	{
-		heap_freetuple(grpstate->grp_firstTuple);
-		grpstate->grp_firstTuple = NULL;
+		heap_freetuple(node->grp_firstTuple);
+		node->grp_firstTuple = NULL;
 	}
 
-	if (((Plan *) node)->lefttree &&
-		((Plan *) node)->lefttree->chgParam == NULL)
-		ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
+	if (((PlanState *) node)->lefttree &&
+		((PlanState *) node)->lefttree->chgParam == NULL)
+		ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
 }
 
 /*****************************************************************************
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index c2c3ab66644..9448ee466a8 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.68 2002/11/30 00:08:15 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.69 2002/12/05 15:50:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,11 +40,10 @@
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
-ExecHash(Hash *node)
+ExecHash(HashState *node)
 {
 	EState	   *estate;
-	HashState  *hashstate;
-	Plan	   *outerNode;
+	PlanState  *outerNode;
 	List	   *hashkeys;
 	HashJoinTable hashtable;
 	TupleTableSlot *slot;
@@ -55,12 +54,10 @@ ExecHash(Hash *node)
 	/*
 	 * get state info from node
 	 */
+	estate = node->ps.state;
+	outerNode = outerPlanState(node);
 
-	hashstate = node->hashstate;
-	estate = node->plan.state;
-	outerNode = outerPlan(node);
-
-	hashtable = hashstate->hashtable;
+	hashtable = node->hashtable;
 	if (hashtable == NULL)
 		elog(ERROR, "ExecHash: hash table is NULL.");
 
@@ -79,15 +76,15 @@ ExecHash(Hash *node)
 	/*
 	 * set expression context
 	 */
-	hashkeys = node->hashkeys;
-	econtext = hashstate->cstate.cs_ExprContext;
+	hashkeys = ((Hash *) node->ps.plan)->hashkeys;
+	econtext = node->ps.ps_ExprContext;
 
 	/*
 	 * get all inner tuples and insert into the hash table (or temp files)
 	 */
 	for (;;)
 	{
-		slot = ExecProcNode(outerNode, (Plan *) node);
+		slot = ExecProcNode(outerNode);
 		if (TupIsNull(slot))
 			break;
 		econtext->ecxt_innertuple = slot;
@@ -108,24 +105,19 @@ ExecHash(Hash *node)
  *		Init routine for Hash node
  * ----------------------------------------------------------------
  */
-bool
-ExecInitHash(Hash *node, EState *estate, Plan *parent)
+HashState *
+ExecInitHash(Hash *node, EState *estate)
 {
 	HashState  *hashstate;
-	Plan	   *outerPlan;
 
 	SO_printf("ExecInitHash: initializing hash node\n");
 
-	/*
-	 * assign the node's execution state
-	 */
-	node->plan.state = estate;
-
 	/*
 	 * create state structure
 	 */
 	hashstate = makeNode(HashState);
-	node->hashstate = hashstate;
+	hashstate->ps.plan = (Plan *) node;
+	hashstate->ps.state = estate;
 	hashstate->hashtable = NULL;
 
 	/*
@@ -133,29 +125,38 @@ ExecInitHash(Hash *node, EState *estate, Plan *parent)
 	 *
 	 * create expression context for node
 	 */
-	ExecAssignExprContext(estate, &hashstate->cstate);
+	ExecAssignExprContext(estate, &hashstate->ps);
 
 #define HASH_NSLOTS 1
 
 	/*
 	 * initialize our result slot
 	 */
-	ExecInitResultTupleSlot(estate, &hashstate->cstate);
+	ExecInitResultTupleSlot(estate, &hashstate->ps);
 
 	/*
-	 * initializes child nodes
+	 * initialize child expressions
 	 */
-	outerPlan = outerPlan(node);
-	ExecInitNode(outerPlan, estate, (Plan *) node);
+	hashstate->ps.targetlist = (List *)
+		ExecInitExpr((Node *) node->plan.targetlist,
+					 (PlanState *) hashstate);
+	hashstate->ps.qual = (List *)
+		ExecInitExpr((Node *) node->plan.qual,
+					 (PlanState *) hashstate);
+
+	/*
+	 * initialize child nodes
+	 */
+	outerPlanState(hashstate) = ExecInitNode(outerPlan(node), estate);
 
 	/*
 	 * initialize tuple type. no need to initialize projection info
 	 * because this node doesn't do projections
 	 */
-	ExecAssignResultTypeFromOuterPlan((Plan *) node, &hashstate->cstate);
-	hashstate->cstate.cs_ProjInfo = NULL;
+	ExecAssignResultTypeFromOuterPlan(&hashstate->ps);
+	hashstate->ps.ps_ProjInfo = NULL;
 
-	return TRUE;
+	return hashstate;
 }
 
 int
@@ -173,28 +174,22 @@ ExecCountSlotsHash(Hash *node)
  * ----------------------------------------------------------------
  */
 void
-ExecEndHash(Hash *node)
+ExecEndHash(HashState *node)
 {
-	HashState  *hashstate;
-	Plan	   *outerPlan;
-
-	/*
-	 * get info from the hash state
-	 */
-	hashstate = node->hashstate;
+	PlanState  *outerPlan;
 
 	/*
 	 * free projection info.  no need to free result type info because
 	 * that came from the outer plan...
 	 */
-	ExecFreeProjectionInfo(&hashstate->cstate);
-	ExecFreeExprContext(&hashstate->cstate);
+	ExecFreeProjectionInfo(&node->ps);
+	ExecFreeExprContext(&node->ps);
 
 	/*
 	 * shut down the subplan
 	 */
-	outerPlan = outerPlan(node);
-	ExecEndNode(outerPlan, (Plan *) node);
+	outerPlan = outerPlanState(node);
+	ExecEndNode(outerPlan);
 }
 
 
@@ -758,12 +753,12 @@ ExecHashTableReset(HashJoinTable hashtable, long ntuples)
 }
 
 void
-ExecReScanHash(Hash *node, ExprContext *exprCtxt, Plan *parent)
+ExecReScanHash(HashState *node, ExprContext *exprCtxt)
 {
 	/*
 	 * if chgParam of subnode is not null then plan will be re-scanned by
 	 * first ExecProcNode.
 	 */
-	if (((Plan *) node)->lefttree->chgParam == NULL)
-		ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
+	if (((PlanState *) node)->lefttree->chgParam == NULL)
+		ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
 }
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 8f0e700ac35..06796c590e3 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.42 2002/11/30 00:08:15 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.43 2002/12/05 15:50:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -22,8 +22,8 @@
 #include "utils/memutils.h"
 
 
-static TupleTableSlot *ExecHashJoinOuterGetTuple(Plan *node, Plan *parent,
-						  HashJoinState *hjstate);
+static TupleTableSlot *ExecHashJoinOuterGetTuple(PlanState *node,
+												 HashJoinState *hjstate);
 static TupleTableSlot *ExecHashJoinGetSavedTuple(HashJoinState *hjstate,
 						  BufFile *file,
 						  TupleTableSlot *tupleSlot);
@@ -41,12 +41,11 @@ static int	ExecHashJoinNewBatch(HashJoinState *hjstate);
  * ----------------------------------------------------------------
  */
 TupleTableSlot *				/* return: a tuple or NULL */
-ExecHashJoin(HashJoin *node)
+ExecHashJoin(HashJoinState *node)
 {
-	HashJoinState *hjstate;
 	EState	   *estate;
-	Plan	   *outerNode;
-	Hash	   *hashNode;
+	PlanState  *outerNode;
+	HashState  *hashNode;
 	List	   *hjclauses;
 	List	   *outerkeys;
 	List	   *joinqual;
@@ -65,37 +64,36 @@ ExecHashJoin(HashJoin *node)
 	/*
 	 * get information from HashJoin node
 	 */
-	hjstate = node->hashjoinstate;
 	hjclauses = node->hashclauses;
-	estate = node->join.plan.state;
-	joinqual = node->join.joinqual;
-	otherqual = node->join.plan.qual;
-	hashNode = (Hash *) innerPlan(node);
-	outerNode = outerPlan(node);
-	hashPhaseDone = hjstate->hj_hashdone;
+	estate = node->js.ps.state;
+	joinqual = node->js.joinqual;
+	otherqual = node->js.ps.qual;
+	hashNode = (HashState *) innerPlanState(node);
+	outerNode = outerPlanState(node);
+	hashPhaseDone = node->hj_hashdone;
 	dir = estate->es_direction;
 
 	/*
 	 * get information from HashJoin state
 	 */
-	hashtable = hjstate->hj_HashTable;
-	outerkeys = hjstate->hj_OuterHashKeys;
-	econtext = hjstate->jstate.cs_ExprContext;
+	hashtable = node->hj_HashTable;
+	outerkeys = node->hj_OuterHashKeys;
+	econtext = node->js.ps.ps_ExprContext;
 
 	/*
 	 * Check to see if we're still projecting out tuples from a previous
 	 * join tuple (because there is a function-returning-set in the
 	 * projection expressions).  If so, try to project another one.
 	 */
-	if (hjstate->jstate.cs_TupFromTlist)
+	if (node->js.ps.ps_TupFromTlist)
 	{
 		TupleTableSlot *result;
 
-		result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
+		result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
 		if (isDone == ExprMultipleResult)
 			return result;
 		/* Done with that source tuple... */
-		hjstate->jstate.cs_TupFromTlist = false;
+		node->js.ps.ps_TupFromTlist = false;
 	}
 
 	/*
@@ -116,16 +114,16 @@ ExecHashJoin(HashJoin *node)
 			/*
 			 * create the hash table
 			 */
-			hashtable = ExecHashTableCreate(hashNode);
-			hjstate->hj_HashTable = hashtable;
+			hashtable = ExecHashTableCreate((Hash *) hashNode->ps.plan);
+			node->hj_HashTable = hashtable;
 
 			/*
 			 * execute the Hash node, to build the hash table
 			 */
-			hashNode->hashstate->hashtable = hashtable;
-			innerTupleSlot = ExecProcNode((Plan *) hashNode, (Plan *) node);
+			hashNode->hashtable = hashtable;
+			innerTupleSlot = ExecProcNode((PlanState *) hashNode);
 		}
-		hjstate->hj_hashdone = true;
+		node->hj_hashdone = true;
 
 		/*
 		 * Open temp files for outer batches, if needed. Note that file
@@ -140,40 +138,39 @@ ExecHashJoin(HashJoin *node)
 	/*
 	 * Now get an outer tuple and probe into the hash table for matches
 	 */
-	outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot;
+	outerTupleSlot = node->js.ps.ps_OuterTupleSlot;
 
 	for (;;)
 	{
 		/*
 		 * If we don't have an outer tuple, get the next one
 		 */
-		if (hjstate->hj_NeedNewOuter)
+		if (node->hj_NeedNewOuter)
 		{
 			outerTupleSlot = ExecHashJoinOuterGetTuple(outerNode,
-													   (Plan *) node,
-													   hjstate);
+													   node);
 			if (TupIsNull(outerTupleSlot))
 			{
 				/*
 				 * when the last batch runs out, clean up and exit
 				 */
 				ExecHashTableDestroy(hashtable);
-				hjstate->hj_HashTable = NULL;
+				node->hj_HashTable = NULL;
 				return NULL;
 			}
 
-			hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
+			node->js.ps.ps_OuterTupleSlot = outerTupleSlot;
 			econtext->ecxt_outertuple = outerTupleSlot;
-			hjstate->hj_NeedNewOuter = false;
-			hjstate->hj_MatchedOuter = false;
+			node->hj_NeedNewOuter = false;
+			node->hj_MatchedOuter = false;
 
 			/*
 			 * now we have an outer tuple, find the corresponding bucket
 			 * for this tuple from the hash table
 			 */
-			hjstate->hj_CurBucketNo = ExecHashGetBucket(hashtable, econtext,
+			node->hj_CurBucketNo = ExecHashGetBucket(hashtable, econtext,
 														outerkeys);
-			hjstate->hj_CurTuple = NULL;
+			node->hj_CurTuple = NULL;
 
 			/*
 			 * Now we've got an outer tuple and the corresponding hash
@@ -182,7 +179,7 @@ ExecHashJoin(HashJoin *node)
 			 */
 			if (hashtable->curbatch == 0)
 			{
-				int			batch = ExecHashJoinGetBatch(hjstate->hj_CurBucketNo,
+				int			batch = ExecHashJoinGetBatch(node->hj_CurBucketNo,
 														 hashtable);
 
 				if (batch > 0)
@@ -196,7 +193,7 @@ ExecHashJoin(HashJoin *node)
 					hashtable->outerBatchSize[batchno]++;
 					ExecHashJoinSaveTuple(outerTupleSlot->val,
 									 hashtable->outerBatchFile[batchno]);
-					hjstate->hj_NeedNewOuter = true;
+					node->hj_NeedNewOuter = true;
 					continue;	/* loop around for a new outer tuple */
 				}
 			}
@@ -207,7 +204,7 @@ ExecHashJoin(HashJoin *node)
 		 */
 		for (;;)
 		{
-			curtuple = ExecScanHashBucket(hjstate,
+			curtuple = ExecScanHashBucket(node,
 										  hjclauses,
 										  econtext);
 			if (curtuple == NULL)
@@ -217,7 +214,7 @@ ExecHashJoin(HashJoin *node)
 			 * we've got a match, but still need to test non-hashed quals
 			 */
 			inntuple = ExecStoreTuple(curtuple,
-									  hjstate->hj_HashTupleSlot,
+									  node->hj_HashTupleSlot,
 									  InvalidBuffer,
 									  false);	/* don't pfree this tuple */
 			econtext->ecxt_innertuple = inntuple;
@@ -235,17 +232,17 @@ ExecHashJoin(HashJoin *node)
 			 */
 			if (ExecQual(joinqual, econtext, false))
 			{
-				hjstate->hj_MatchedOuter = true;
+				node->hj_MatchedOuter = true;
 
 				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
 				{
 					TupleTableSlot *result;
 
-					result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
+					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
 
 					if (isDone != ExprEndResult)
 					{
-						hjstate->jstate.cs_TupFromTlist =
+						node->js.ps.ps_TupFromTlist =
 							(isDone == ExprMultipleResult);
 						return result;
 					}
@@ -258,10 +255,10 @@ ExecHashJoin(HashJoin *node)
 		 * whether to emit a dummy outer-join tuple. If not, loop around
 		 * to get a new outer tuple.
 		 */
-		hjstate->hj_NeedNewOuter = true;
+		node->hj_NeedNewOuter = true;
 
-		if (!hjstate->hj_MatchedOuter &&
-			node->join.jointype == JOIN_LEFT)
+		if (!node->hj_MatchedOuter &&
+			node->js.jointype == JOIN_LEFT)
 		{
 			/*
 			 * We are doing an outer join and there were no join matches
@@ -269,7 +266,7 @@ ExecHashJoin(HashJoin *node)
 			 * nulls for the inner tuple, and return it if it passes the
 			 * non-join quals.
 			 */
-			econtext->ecxt_innertuple = hjstate->hj_NullInnerTupleSlot;
+			econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
 			if (ExecQual(otherqual, econtext, false))
 			{
@@ -280,11 +277,11 @@ ExecHashJoin(HashJoin *node)
 				 */
 				TupleTableSlot *result;
 
-				result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
+				result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
 
 				if (isDone != ExprEndResult)
 				{
-					hjstate->jstate.cs_TupFromTlist =
+					node->js.ps.ps_TupFromTlist =
 						(isDone == ExprMultipleResult);
 					return result;
 				}
@@ -299,47 +296,60 @@ ExecHashJoin(HashJoin *node)
  *		Init routine for HashJoin node.
  * ----------------------------------------------------------------
  */
-bool							/* return: initialization status */
-ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
+HashJoinState *
+ExecInitHashJoin(HashJoin *node, EState *estate)
 {
 	HashJoinState *hjstate;
 	Plan	   *outerNode;
 	Hash	   *hashNode;
 	List	   *hcl;
 
-	/*
-	 * assign the node's execution state
-	 */
-	node->join.plan.state = estate;
-
 	/*
 	 * create state structure
 	 */
 	hjstate = makeNode(HashJoinState);
-	node->hashjoinstate = hjstate;
+	hjstate->js.ps.plan = (Plan *) node;
+	hjstate->js.ps.state = estate;
 
 	/*
 	 * Miscellaneous initialization
 	 *
 	 * create expression context for node
 	 */
-	ExecAssignExprContext(estate, &hjstate->jstate);
+	ExecAssignExprContext(estate, &hjstate->js.ps);
+
+	/*
+	 * initialize child expressions
+	 */
+	hjstate->js.ps.targetlist = (List *)
+		ExecInitExpr((Node *) node->join.plan.targetlist,
+					 (PlanState *) hjstate);
+	hjstate->js.ps.qual = (List *)
+		ExecInitExpr((Node *) node->join.plan.qual,
+					 (PlanState *) hjstate);
+	hjstate->js.jointype = node->join.jointype;
+	hjstate->js.joinqual = (List *)
+		ExecInitExpr((Node *) node->join.joinqual,
+					 (PlanState *) hjstate);
+	hjstate->hashclauses = (List *)
+		ExecInitExpr((Node *) node->hashclauses,
+					 (PlanState *) hjstate);
 
 	/*
-	 * initializes child nodes
+	 * initialize child nodes
 	 */
-	outerNode = outerPlan((Plan *) node);
-	hashNode = (Hash *) innerPlan((Plan *) node);
+	outerNode = outerPlan(node);
+	hashNode = (Hash *) innerPlan(node);
 
-	ExecInitNode(outerNode, estate, (Plan *) node);
-	ExecInitNode((Plan *) hashNode, estate, (Plan *) node);
+	outerPlanState(hjstate) = ExecInitNode(outerNode, estate);
+	innerPlanState(hjstate) = ExecInitNode((Plan *) hashNode, estate);
 
 #define HASHJOIN_NSLOTS 3
 
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &hjstate->jstate);
+	ExecInitResultTupleSlot(estate, &hjstate->js.ps);
 	hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate);
 
 	switch (node->join.jointype)
@@ -349,7 +359,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
 		case JOIN_LEFT:
 			hjstate->hj_NullInnerTupleSlot =
 				ExecInitNullTupleSlot(estate,
-									  ExecGetTupType((Plan *) hashNode));
+									  ExecGetTupType(innerPlanState(hjstate)));
 			break;
 		default:
 			elog(ERROR, "ExecInitHashJoin: unsupported join type %d",
@@ -364,8 +374,8 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
 	 * the contents of the hash table.	-cim 6/9/91
 	 */
 	{
-		HashState  *hashstate = hashNode->hashstate;
-		TupleTableSlot *slot = hashstate->cstate.cs_ResultTupleSlot;
+		HashState  *hashstate = (HashState *) innerPlanState(hjstate);
+		TupleTableSlot *slot = hashstate->ps.ps_ResultTupleSlot;
 
 		hjstate->hj_HashTupleSlot = slot;
 	}
@@ -373,11 +383,11 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
 	/*
 	 * initialize tuple type and projection info
 	 */
-	ExecAssignResultTypeFromTL((Plan *) node, &hjstate->jstate);
-	ExecAssignProjectionInfo((Plan *) node, &hjstate->jstate);
+	ExecAssignResultTypeFromTL(&hjstate->js.ps);
+	ExecAssignProjectionInfo(&hjstate->js.ps);
 
 	ExecSetSlotDescriptor(hjstate->hj_OuterTupleSlot,
-						  ExecGetTupType(outerNode),
+						  ExecGetTupType(outerPlanState(hjstate)),
 						  false);
 
 	/*
@@ -402,12 +412,12 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
 											get_leftop(lfirst(hcl)));
 	}
 
-	hjstate->jstate.cs_OuterTupleSlot = NULL;
-	hjstate->jstate.cs_TupFromTlist = false;
+	hjstate->js.ps.ps_OuterTupleSlot = NULL;
+	hjstate->js.ps.ps_TupFromTlist = false;
 	hjstate->hj_NeedNewOuter = true;
 	hjstate->hj_MatchedOuter = false;
 
-	return TRUE;
+	return hjstate;
 }
 
 int
@@ -425,46 +435,35 @@ ExecCountSlotsHashJoin(HashJoin *node)
  * ----------------------------------------------------------------
  */
 void
-ExecEndHashJoin(HashJoin *node)
+ExecEndHashJoin(HashJoinState *node)
 {
-	HashJoinState *hjstate;
-
-	/*
-	 * get info from the HashJoin state
-	 */
-	hjstate = node->hashjoinstate;
-
 	/*
 	 * free hash table in case we end plan before all tuples are retrieved
 	 */
-	if (hjstate->hj_HashTable)
+	if (node->hj_HashTable)
 	{
-		ExecHashTableDestroy(hjstate->hj_HashTable);
-		hjstate->hj_HashTable = NULL;
+		ExecHashTableDestroy(node->hj_HashTable);
+		node->hj_HashTable = NULL;
 	}
 
 	/*
 	 * Free the projection info and the scan attribute info
-	 *
-	 * Note: we don't ExecFreeResultType(hjstate) because the rule manager
-	 * depends on the tupType returned by ExecMain().  So for now, this is
-	 * freed at end-transaction time.  -cim 6/2/91
 	 */
-	ExecFreeProjectionInfo(&hjstate->jstate);
-	ExecFreeExprContext(&hjstate->jstate);
+	ExecFreeProjectionInfo(&node->js.ps);
+	ExecFreeExprContext(&node->js.ps);
 
 	/*
 	 * clean up subtrees
 	 */
-	ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
-	ExecEndNode(innerPlan((Plan *) node), (Plan *) node);
+	ExecEndNode(outerPlanState(node));
+	ExecEndNode(innerPlanState(node));
 
 	/*
 	 * clean out the tuple table
 	 */
-	ExecClearTuple(hjstate->jstate.cs_ResultTupleSlot);
-	ExecClearTuple(hjstate->hj_OuterTupleSlot);
-	ExecClearTuple(hjstate->hj_HashTupleSlot);
+	ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
+	ExecClearTuple(node->hj_OuterTupleSlot);
+	ExecClearTuple(node->hj_HashTupleSlot);
 
 }
 
@@ -478,7 +477,7 @@ ExecEndHashJoin(HashJoin *node)
  */
 
 static TupleTableSlot *
-ExecHashJoinOuterGetTuple(Plan *node, Plan *parent, HashJoinState *hjstate)
+ExecHashJoinOuterGetTuple(PlanState *node, HashJoinState *hjstate)
 {
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	int			curbatch = hashtable->curbatch;
@@ -486,7 +485,7 @@ ExecHashJoinOuterGetTuple(Plan *node, Plan *parent, HashJoinState *hjstate)
 
 	if (curbatch == 0)
 	{							/* if it is the first pass */
-		slot = ExecProcNode(node, parent);
+		slot = ExecProcNode(node);
 		if (!TupIsNull(slot))
 			return slot;
 
@@ -611,7 +610,7 @@ ExecHashJoinNewBatch(HashJoinState *hjstate)
 	 */
 	ExecHashTableReset(hashtable, innerBatchSize[newbatch - 1]);
 
-	econtext = hjstate->jstate.cs_ExprContext;
+	econtext = hjstate->js.ps.ps_ExprContext;
 	innerhashkeys = hjstate->hj_InnerHashKeys;
 
 	while ((slot = ExecHashJoinGetSavedTuple(hjstate,
@@ -682,39 +681,37 @@ ExecHashJoinSaveTuple(HeapTuple heapTuple,
 }
 
 void
-ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent)
+ExecReScanHashJoin(HashJoinState *node, ExprContext *exprCtxt)
 {
-	HashJoinState *hjstate = node->hashjoinstate;
-
-	if (!hjstate->hj_hashdone)
+	if (!node->hj_hashdone)
 		return;
 
-	hjstate->hj_hashdone = false;
+	node->hj_hashdone = false;
 
 	/*
 	 * Unfortunately, currently we have to destroy hashtable in all
 	 * cases...
 	 */
-	if (hjstate->hj_HashTable)
+	if (node->hj_HashTable)
 	{
-		ExecHashTableDestroy(hjstate->hj_HashTable);
-		hjstate->hj_HashTable = NULL;
+		ExecHashTableDestroy(node->hj_HashTable);
+		node->hj_HashTable = NULL;
 	}
 
-	hjstate->hj_CurBucketNo = 0;
-	hjstate->hj_CurTuple = (HashJoinTuple) NULL;
+	node->hj_CurBucketNo = 0;
+	node->hj_CurTuple = (HashJoinTuple) NULL;
 
-	hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
-	hjstate->jstate.cs_TupFromTlist = false;
-	hjstate->hj_NeedNewOuter = true;
-	hjstate->hj_MatchedOuter = false;
+	node->js.ps.ps_OuterTupleSlot = (TupleTableSlot *) NULL;
+	node->js.ps.ps_TupFromTlist = false;
+	node->hj_NeedNewOuter = true;
+	node->hj_MatchedOuter = false;
 
 	/*
 	 * if chgParam of subnodes is not null then plans will be re-scanned
 	 * by first ExecProcNode.
 	 */
-	if (((Plan *) node)->lefttree->chgParam == NULL)
-		ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
-	if (((Plan *) node)->righttree->chgParam == NULL)
-		ExecReScan(((Plan *) node)->righttree, exprCtxt, (Plan *) node);
+	if (((PlanState *) node)->lefttree->chgParam == NULL)
+		ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
+	if (((PlanState *) node)->righttree->chgParam == NULL)
+		ExecReScan(((PlanState *) node)->righttree, exprCtxt);
 }
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 78f5ad0ba26..e9888c4d3f5 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.71 2002/09/04 20:31:18 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.72 2002/12/05 15:50:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -40,7 +40,7 @@
 #define LEFT_OP			1
 #define RIGHT_OP		2
 
-static TupleTableSlot *IndexNext(IndexScan *node);
+static TupleTableSlot *IndexNext(IndexScanState *node);
 
 /* ----------------------------------------------------------------
  *		IndexNext
@@ -65,15 +65,14 @@ static TupleTableSlot *IndexNext(IndexScan *node);
  * ----------------------------------------------------------------
  */
 static TupleTableSlot *
-IndexNext(IndexScan *node)
+IndexNext(IndexScanState *node)
 {
 	EState	   *estate;
-	CommonScanState *scanstate;
-	IndexScanState *indexstate;
 	ExprContext *econtext;
 	ScanDirection direction;
 	IndexScanDescPtr scanDescs;
 	IndexScanDesc scandesc;
+	Index		scanrelid;
 	HeapTuple	tuple;
 	TupleTableSlot *slot;
 	int			numIndices;
@@ -83,21 +82,20 @@ IndexNext(IndexScan *node)
 	/*
 	 * extract necessary information from index scan node
 	 */
-	estate = node->scan.plan.state;
+	estate = node->ss.ps.state;
 	direction = estate->es_direction;
-	if (ScanDirectionIsBackward(node->indxorderdir))
+	if (ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indxorderdir))
 	{
 		if (ScanDirectionIsForward(direction))
 			direction = BackwardScanDirection;
 		else if (ScanDirectionIsBackward(direction))
 			direction = ForwardScanDirection;
 	}
-	scanstate = node->scan.scanstate;
-	indexstate = node->indxstate;
-	scanDescs = indexstate->iss_ScanDescs;
-	numIndices = indexstate->iss_NumIndices;
-	econtext = scanstate->cstate.cs_ExprContext;
-	slot = scanstate->css_ScanTupleSlot;
+	scanDescs = node->iss_ScanDescs;
+	numIndices = node->iss_NumIndices;
+	econtext = node->ss.ps.ps_ExprContext;
+	slot = node->ss.ss_ScanTupleSlot;
+	scanrelid = ((IndexScan *) node->ss.ps.plan)->scan.scanrelid;
 
 	/*
 	 * Check if we are evaluating PlanQual for tuple of this relation.
@@ -106,15 +104,15 @@ IndexNext(IndexScan *node)
 	 * switching in Init/ReScan plan...
 	 */
 	if (estate->es_evTuple != NULL &&
-		estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
+		estate->es_evTuple[scanrelid - 1] != NULL)
 	{
 		List	   *qual;
 
 		ExecClearTuple(slot);
-		if (estate->es_evTupleNull[node->scan.scanrelid - 1])
+		if (estate->es_evTupleNull[scanrelid - 1])
 			return slot;		/* return empty slot */
 
-		ExecStoreTuple(estate->es_evTuple[node->scan.scanrelid - 1],
+		ExecStoreTuple(estate->es_evTuple[scanrelid - 1],
 					   slot, InvalidBuffer, false);
 
 		/* Does the tuple meet any of the OR'd indxqual conditions? */
@@ -131,7 +129,7 @@ IndexNext(IndexScan *node)
 			slot->val = NULL;
 
 		/* Flag for the next call that no more tuples */
-		estate->es_evTupleNull[node->scan.scanrelid - 1] = true;
+		estate->es_evTupleNull[scanrelid - 1] = true;
 
 		return slot;
 	}
@@ -144,24 +142,24 @@ IndexNext(IndexScan *node)
 	bBackward = ScanDirectionIsBackward(direction);
 	if (bBackward)
 	{
-		indexNumber = numIndices - indexstate->iss_IndexPtr - 1;
+		indexNumber = numIndices - node->iss_IndexPtr - 1;
 		if (indexNumber < 0)
 		{
 			indexNumber = 0;
-			indexstate->iss_IndexPtr = numIndices - 1;
+			node->iss_IndexPtr = numIndices - 1;
 		}
 	}
 	else
 	{
-		if ((indexNumber = indexstate->iss_IndexPtr) < 0)
+		if ((indexNumber = node->iss_IndexPtr) < 0)
 		{
 			indexNumber = 0;
-			indexstate->iss_IndexPtr = 0;
+			node->iss_IndexPtr = 0;
 		}
 	}
 	while (indexNumber < numIndices)
 	{
-		scandesc = scanDescs[indexstate->iss_IndexPtr];
+		scandesc = scanDescs[node->iss_IndexPtr];
 		while ((tuple = index_getnext(scandesc, direction)) != NULL)
 		{
 			/*
@@ -181,7 +179,7 @@ IndexNext(IndexScan *node)
 			 * We do this by passing the tuple through ExecQual and
 			 * checking for failure with all previous qualifications.
 			 */
-			if (indexstate->iss_IndexPtr > 0)
+			if (node->iss_IndexPtr > 0)
 			{
 				bool		prev_matches = false;
 				int			prev_index;
@@ -191,7 +189,7 @@ IndexNext(IndexScan *node)
 				ResetExprContext(econtext);
 				qual = node->indxqualorig;
 				for (prev_index = 0;
-					 prev_index < indexstate->iss_IndexPtr;
+					 prev_index < node->iss_IndexPtr;
 					 prev_index++)
 				{
 					if (ExecQual((List *) lfirst(qual), econtext, false))
@@ -216,9 +214,9 @@ IndexNext(IndexScan *node)
 		{
 			indexNumber++;
 			if (bBackward)
-				indexstate->iss_IndexPtr--;
+				node->iss_IndexPtr--;
 			else
-				indexstate->iss_IndexPtr++;
+				node->iss_IndexPtr++;
 		}
 	}
 
@@ -251,21 +249,19 @@ IndexNext(IndexScan *node)
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
-ExecIndexScan(IndexScan *node)
+ExecIndexScan(IndexScanState *node)
 {
-	IndexScanState *indexstate = node->indxstate;
-
 	/*
 	 * If we have runtime keys and they've not already been set up, do it
 	 * now.
 	 */
-	if (indexstate->iss_RuntimeKeyInfo && !indexstate->iss_RuntimeKeysReady)
-		ExecReScan((Plan *) node, NULL, NULL);
+	if (node->iss_RuntimeKeyInfo && !node->iss_RuntimeKeysReady)
+		ExecReScan((PlanState *) node, NULL);
 
 	/*
 	 * use IndexNext as access method
 	 */
-	return ExecScan(&node->scan, (ExecScanAccessMtd) IndexNext);
+	return ExecScan(&node->ss, (ExecScanAccessMtd) IndexNext);
 }
 
 /* ----------------------------------------------------------------
@@ -280,28 +276,27 @@ ExecIndexScan(IndexScan *node)
  * ----------------------------------------------------------------
  */
 void
-ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
+ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
 {
 	EState	   *estate;
-	IndexScanState *indexstate;
 	ExprContext *econtext;
 	int			numIndices;
 	IndexScanDescPtr scanDescs;
 	ScanKey    *scanKeys;
 	int		  **runtimeKeyInfo;
 	int		   *numScanKeys;
+	Index		scanrelid;
 	int			i;
 	int			j;
 
-	estate = node->scan.plan.state;
-	indexstate = node->indxstate;
-	econtext = indexstate->iss_RuntimeContext;	/* context for runtime
-												 * keys */
-	numIndices = indexstate->iss_NumIndices;
-	scanDescs = indexstate->iss_ScanDescs;
-	scanKeys = indexstate->iss_ScanKeys;
-	runtimeKeyInfo = indexstate->iss_RuntimeKeyInfo;
-	numScanKeys = indexstate->iss_NumScanKeys;
+	estate = node->ss.ps.state;
+	econtext = node->iss_RuntimeContext;	/* context for runtime keys */
+	numIndices = node->iss_NumIndices;
+	scanDescs = node->iss_ScanDescs;
+	scanKeys = node->iss_ScanKeys;
+	runtimeKeyInfo = node->iss_RuntimeKeyInfo;
+	numScanKeys = node->iss_NumScanKeys;
+	scanrelid = ((IndexScan *) node->ss.ps.plan)->scan.scanrelid;
 
 	if (econtext)
 	{
@@ -315,7 +310,7 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
 			ExprContext *stdecontext;
 
 			econtext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
-			stdecontext = node->scan.scanstate->cstate.cs_ExprContext;
+			stdecontext = node->ss.ps.ps_ExprContext;
 			stdecontext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
 		}
 
@@ -392,22 +387,22 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
 			}
 		}
 
-		indexstate->iss_RuntimeKeysReady = true;
+		node->iss_RuntimeKeysReady = true;
 	}
 
 	/* If this is re-scanning of PlanQual ... */
 	if (estate->es_evTuple != NULL &&
-		estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
+		estate->es_evTuple[scanrelid - 1] != NULL)
 	{
-		estate->es_evTupleNull[node->scan.scanrelid - 1] = false;
+		estate->es_evTupleNull[scanrelid - 1] = false;
 		return;
 	}
 
 	/* reset index scans */
-	if (ScanDirectionIsBackward(node->indxorderdir))
-		indexstate->iss_IndexPtr = numIndices;
+	if (ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indxorderdir))
+		node->iss_IndexPtr = numIndices;
 	else
-		indexstate->iss_IndexPtr = -1;
+		node->iss_IndexPtr = -1;
 
 	for (i = 0; i < numIndices; i++)
 	{
@@ -427,13 +422,10 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
  * ----------------------------------------------------------------
  */
 void
-ExecEndIndexScan(IndexScan *node)
+ExecEndIndexScan(IndexScanState *node)
 {
-	CommonScanState *scanstate;
-	IndexScanState *indexstate;
 	int		  **runtimeKeyInfo;
 	ScanKey    *scanKeys;
-	List	   *indxqual;
 	int		   *numScanKeys;
 	int			numIndices;
 	Relation	relation;
@@ -441,32 +433,25 @@ ExecEndIndexScan(IndexScan *node)
 	IndexScanDescPtr indexScanDescs;
 	int			i;
 
-	scanstate = node->scan.scanstate;
-	indexstate = node->indxstate;
-	indxqual = node->indxqual;
-	runtimeKeyInfo = indexstate->iss_RuntimeKeyInfo;
+	runtimeKeyInfo = node->iss_RuntimeKeyInfo;
 
 	/*
 	 * extract information from the node
 	 */
-	numIndices = indexstate->iss_NumIndices;
-	scanKeys = indexstate->iss_ScanKeys;
-	numScanKeys = indexstate->iss_NumScanKeys;
-	indexRelationDescs = indexstate->iss_RelationDescs;
-	indexScanDescs = indexstate->iss_ScanDescs;
-	relation = scanstate->css_currentRelation;
+	numIndices = node->iss_NumIndices;
+	scanKeys = node->iss_ScanKeys;
+	numScanKeys = node->iss_NumScanKeys;
+	indexRelationDescs = node->iss_RelationDescs;
+	indexScanDescs = node->iss_ScanDescs;
+	relation = node->ss.ss_currentRelation;
 
 	/*
 	 * Free the projection info and the scan attribute info
-	 *
-	 * Note: we don't ExecFreeResultType(scanstate) because the rule manager
-	 * depends on the tupType returned by ExecMain().  So for now, this is
-	 * freed at end-transaction time.  -cim 6/2/91
 	 */
-	ExecFreeProjectionInfo(&scanstate->cstate);
-	ExecFreeExprContext(&scanstate->cstate);
-	if (indexstate->iss_RuntimeContext)
-		FreeExprContext(indexstate->iss_RuntimeContext);
+	ExecFreeProjectionInfo(&node->ss.ps);
+	ExecFreeExprContext(&node->ss.ps);
+	if (node->iss_RuntimeContext)
+		FreeExprContext(node->iss_RuntimeContext);
 
 	/*
 	 * close the index relations
@@ -514,12 +499,11 @@ ExecEndIndexScan(IndexScan *node)
 	/*
 	 * clear out tuple table slots
 	 */
-	ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
-	ExecClearTuple(scanstate->css_ScanTupleSlot);
-	pfree(scanstate);
-	pfree(indexstate->iss_RelationDescs);
-	pfree(indexstate->iss_ScanDescs);
-	pfree(indexstate);
+	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+	ExecClearTuple(node->ss.ss_ScanTupleSlot);
+	pfree(node->iss_RelationDescs);
+	pfree(node->iss_ScanDescs);
+	pfree(node);
 }
 
 /* ----------------------------------------------------------------
@@ -531,21 +515,16 @@ ExecEndIndexScan(IndexScan *node)
  * ----------------------------------------------------------------
  */
 void
-ExecIndexMarkPos(IndexScan *node)
+ExecIndexMarkPos(IndexScanState *node)
 {
-	IndexScanState *indexstate;
 	IndexScanDescPtr indexScanDescs;
 	IndexScanDesc scanDesc;
 	int			indexPtr;
 
-	indexstate = node->indxstate;
-	indexPtr = indexstate->iss_MarkIndexPtr = indexstate->iss_IndexPtr;
-	indexScanDescs = indexstate->iss_ScanDescs;
+	indexPtr = node->iss_MarkIndexPtr = node->iss_IndexPtr;
+	indexScanDescs = node->iss_ScanDescs;
 	scanDesc = indexScanDescs[indexPtr];
 
-#ifdef NOT_USED
-	IndexScanMarkPosition(scanDesc);
-#endif
 	index_markpos(scanDesc);
 }
 
@@ -560,21 +539,16 @@ ExecIndexMarkPos(IndexScan *node)
  * ----------------------------------------------------------------
  */
 void
-ExecIndexRestrPos(IndexScan *node)
+ExecIndexRestrPos(IndexScanState *node)
 {
-	IndexScanState *indexstate;
 	IndexScanDescPtr indexScanDescs;
 	IndexScanDesc scanDesc;
 	int			indexPtr;
 
-	indexstate = node->indxstate;
-	indexPtr = indexstate->iss_IndexPtr = indexstate->iss_MarkIndexPtr;
-	indexScanDescs = indexstate->iss_ScanDescs;
+	indexPtr = node->iss_IndexPtr = node->iss_MarkIndexPtr;
+	indexScanDescs = node->iss_ScanDescs;
 	scanDesc = indexScanDescs[indexPtr];
 
-#ifdef NOT_USED
-	IndexScanRestorePosition(scanDesc);
-#endif
 	index_restrpos(scanDesc);
 }
 
@@ -597,11 +571,10 @@ ExecIndexRestrPos(IndexScan *node)
  *		  estate: the execution state initialized in InitPlan.
  * ----------------------------------------------------------------
  */
-bool
-ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
+IndexScanState *
+ExecInitIndexScan(IndexScan *node, EState *estate)
 {
 	IndexScanState *indexstate;
-	CommonScanState *scanstate;
 	List	   *indxqual;
 	List	   *indxid;
 	List	   *listscan;
@@ -620,45 +593,52 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	Relation	currentRelation;
 
 	/*
-	 * assign execution state to node
+	 * create state structure
 	 */
-	node->scan.plan.state = estate;
+	indexstate = makeNode(IndexScanState);
+	indexstate->ss.ps.plan = (Plan *) node;
+	indexstate->ss.ps.state = estate;
 
 	/*
-	 * Part 1)	initialize scan state
+	 * Miscellaneous initialization
 	 *
-	 * create new CommonScanState for node
+	 * create expression context for node
 	 */
-	scanstate = makeNode(CommonScanState);
-	node->scan.scanstate = scanstate;
+	ExecAssignExprContext(estate, &indexstate->ss.ps);
 
 	/*
-	 * Miscellaneous initialization
-	 *
-	 * create expression context for node
+	 * initialize child expressions
 	 */
-	ExecAssignExprContext(estate, &scanstate->cstate);
+	indexstate->ss.ps.targetlist = (List *)
+		ExecInitExpr((Node *) node->scan.plan.targetlist,
+					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual = (List *)
+		ExecInitExpr((Node *) node->scan.plan.qual,
+					 (PlanState *) indexstate);
+	indexstate->indxqual = (List *)
+		ExecInitExpr((Node *) node->indxqual,
+					 (PlanState *) indexstate);
+	indexstate->indxqualorig = (List *)
+		ExecInitExpr((Node *) node->indxqualorig,
+					 (PlanState *) indexstate);
 
 #define INDEXSCAN_NSLOTS 2
 
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &scanstate->cstate);
-	ExecInitScanTupleSlot(estate, scanstate);
+	ExecInitResultTupleSlot(estate, &indexstate->ss.ps);
+	ExecInitScanTupleSlot(estate, &indexstate->ss);
 
 	/*
 	 * initialize projection info.	result type comes from scan desc
 	 * below..
 	 */
-	ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate);
+	ExecAssignProjectionInfo(&indexstate->ss.ps);
 
 	/*
-	 * Part 2)	initialize index scan state
-	 *
-	 * create new IndexScanState for node
+	 * Initialize index-specific scan state
 	 */
-	indexstate = makeNode(IndexScanState);
 	indexstate->iss_NumIndices = 0;
 	indexstate->iss_IndexPtr = -1;
 	indexstate->iss_ScanKeys = NULL;
@@ -669,8 +649,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	indexstate->iss_RelationDescs = NULL;
 	indexstate->iss_ScanDescs = NULL;
 
-	node->indxstate = indexstate;
-
 	/*
 	 * get the index node information
 	 */
@@ -836,7 +814,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 				{
 					/* treat Param like a constant */
 					scanvalue = ExecEvalParam((Param *) leftop,
-										scanstate->cstate.cs_ExprContext,
+										indexstate->ss.ps.ps_ExprContext,
 											  &isnull);
 					if (isnull)
 						flags |= SK_ISNULL;
@@ -911,7 +889,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 				{
 					/* treat Param like a constant */
 					scanvalue = ExecEvalParam((Param *) rightop,
-										scanstate->cstate.cs_ExprContext,
+										indexstate->ss.ps.ps_ExprContext,
 											  &isnull);
 					if (isnull)
 						flags |= SK_ISNULL;
@@ -976,12 +954,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	 */
 	if (have_runtime_keys)
 	{
-		ExprContext *stdecontext = scanstate->cstate.cs_ExprContext;
+		ExprContext *stdecontext = indexstate->ss.ps.ps_ExprContext;
 
-		ExecAssignExprContext(estate, &scanstate->cstate);
+		ExecAssignExprContext(estate, &indexstate->ss.ps);
 		indexstate->iss_RuntimeKeyInfo = runtimeKeyInfo;
-		indexstate->iss_RuntimeContext = scanstate->cstate.cs_ExprContext;
-		scanstate->cstate.cs_ExprContext = stdecontext;
+		indexstate->iss_RuntimeContext = indexstate->ss.ps.ps_ExprContext;
+		indexstate->ss.ps.ps_ExprContext = stdecontext;
 	}
 	else
 	{
@@ -1008,14 +986,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	if (!RelationGetForm(currentRelation)->relhasindex)
 		elog(ERROR, "indexes of the relation %u was inactivated", reloid);
 
-	scanstate->css_currentRelation = currentRelation;
-	scanstate->css_currentScanDesc = NULL;		/* no heap scan here */
+	indexstate->ss.ss_currentRelation = currentRelation;
+	indexstate->ss.ss_currentScanDesc = NULL;		/* no heap scan here */
 
 	/*
 	 * get the scan type from the relation descriptor.
 	 */
-	ExecAssignScanType(scanstate, RelationGetDescr(currentRelation), false);
-	ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
+	ExecAssignScanType(&indexstate->ss, RelationGetDescr(currentRelation), false);
+	ExecAssignResultTypeFromTL(&indexstate->ss.ps);
 
 	/*
 	 * open the index relations and initialize relation and scan
@@ -1043,7 +1021,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
 	/*
 	 * all done.
 	 */
-	return TRUE;
+	return indexstate;
 }
 
 int
diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c
index 4b22b93d579..1ea3aa62b52 100644
--- a/src/backend/executor/nodeLimit.c
+++ b/src/backend/executor/nodeLimit.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeLimit.c,v 1.11 2002/11/22 22:10:01 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeLimit.c,v 1.12 2002/12/05 15:50:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,7 +24,7 @@
 #include "executor/executor.h"
 #include "executor/nodeLimit.h"
 
-static void recompute_limits(Limit *node);
+static void recompute_limits(LimitState *node);
 
 
 /* ----------------------------------------------------------------
@@ -35,26 +35,24 @@ static void recompute_limits(Limit *node);
  * ----------------------------------------------------------------
  */
 TupleTableSlot *				/* return: a tuple or NULL */
-ExecLimit(Limit *node)
+ExecLimit(LimitState *node)
 {
-	LimitState *limitstate;
 	ScanDirection direction;
 	TupleTableSlot *resultTupleSlot;
 	TupleTableSlot *slot;
-	Plan	   *outerPlan;
+	PlanState  *outerPlan;
 
 	/*
 	 * get information from the node
 	 */
-	limitstate = node->limitstate;
-	direction = node->plan.state->es_direction;
-	outerPlan = outerPlan((Plan *) node);
-	resultTupleSlot = limitstate->cstate.cs_ResultTupleSlot;
+	direction = node->ps.state->es_direction;
+	outerPlan = outerPlanState(node);
+	resultTupleSlot = node->ps.ps_ResultTupleSlot;
 
 	/*
 	 * The main logic is a simple state machine.
 	 */
-	switch (limitstate->lstate)
+	switch (node->lstate)
 	{
 		case LIMIT_INITIAL:
 			/*
@@ -71,9 +69,9 @@ ExecLimit(Limit *node)
 			/*
 			 * Check for empty window; if so, treat like empty subplan.
 			 */
-			if (limitstate->count <= 0 && !limitstate->noCount)
+			if (node->count <= 0 && !node->noCount)
 			{
-				limitstate->lstate = LIMIT_EMPTY;
+				node->lstate = LIMIT_EMPTY;
 				return NULL;
 			}
 			/*
@@ -81,24 +79,24 @@ ExecLimit(Limit *node)
 			 */
 			for (;;)
 			{
-				slot = ExecProcNode(outerPlan, (Plan *) node);
+				slot = ExecProcNode(outerPlan);
 				if (TupIsNull(slot))
 				{
 					/*
 					 * The subplan returns too few tuples for us to produce
 					 * any output at all.
 					 */
-					limitstate->lstate = LIMIT_EMPTY;
+					node->lstate = LIMIT_EMPTY;
 					return NULL;
 				}
-				limitstate->subSlot = slot;
-				if (++limitstate->position > limitstate->offset)
+				node->subSlot = slot;
+				if (++node->position > node->offset)
 					break;
 			}
 			/*
 			 * Okay, we have the first tuple of the window.
 			 */
-			limitstate->lstate = LIMIT_INWINDOW;
+			node->lstate = LIMIT_INWINDOW;
 			break;
 
 		case LIMIT_EMPTY:
@@ -117,23 +115,23 @@ ExecLimit(Limit *node)
 				 * advancing the subplan or the position variable; but
 				 * change the state machine state to record having done so.
 				 */
-				if (!limitstate->noCount &&
-					limitstate->position >= limitstate->offset + limitstate->count)
+				if (!node->noCount &&
+					node->position >= node->offset + node->count)
 				{
-					limitstate->lstate = LIMIT_WINDOWEND;
+					node->lstate = LIMIT_WINDOWEND;
 					return NULL;
 				}
 				/*
 				 * Get next tuple from subplan, if any.
 				 */
-				slot = ExecProcNode(outerPlan, (Plan *) node);
+				slot = ExecProcNode(outerPlan);
 				if (TupIsNull(slot))
 				{
-					limitstate->lstate = LIMIT_SUBPLANEOF;
+					node->lstate = LIMIT_SUBPLANEOF;
 					return NULL;
 				}
-				limitstate->subSlot = slot;
-				limitstate->position++;
+				node->subSlot = slot;
+				node->position++;
 			}
 			else
 			{
@@ -141,19 +139,19 @@ ExecLimit(Limit *node)
 				 * Backwards scan, so check for stepping off start of window.
 				 * As above, change only state-machine status if so.
 				 */
-				if (limitstate->position <= limitstate->offset + 1)
+				if (node->position <= node->offset + 1)
 				{
-					limitstate->lstate = LIMIT_WINDOWSTART;
+					node->lstate = LIMIT_WINDOWSTART;
 					return NULL;
 				}
 				/*
 				 * Get previous tuple from subplan; there should be one!
 				 */
-				slot = ExecProcNode(outerPlan, (Plan *) node);
+				slot = ExecProcNode(outerPlan);
 				if (TupIsNull(slot))
 					elog(ERROR, "ExecLimit: subplan failed to run backwards");
-				limitstate->subSlot = slot;
-				limitstate->position--;
+				node->subSlot = slot;
+				node->position--;
 			}
 			break;
 
@@ -164,11 +162,11 @@ ExecLimit(Limit *node)
 			 * Backing up from subplan EOF, so re-fetch previous tuple;
 			 * there should be one!  Note previous tuple must be in window.
 			 */
-			slot = ExecProcNode(outerPlan, (Plan *) node);
+			slot = ExecProcNode(outerPlan);
 			if (TupIsNull(slot))
 				elog(ERROR, "ExecLimit: subplan failed to run backwards");
-			limitstate->subSlot = slot;
-			limitstate->lstate = LIMIT_INWINDOW;
+			node->subSlot = slot;
+			node->lstate = LIMIT_INWINDOW;
 			/* position does not change 'cause we didn't advance it before */
 			break;
 
@@ -179,8 +177,8 @@ ExecLimit(Limit *node)
 			 * Backing up from window end: simply re-return the last
 			 * tuple fetched from the subplan.
 			 */
-			slot = limitstate->subSlot;
-			limitstate->lstate = LIMIT_INWINDOW;
+			slot = node->subSlot;
+			node->lstate = LIMIT_INWINDOW;
 			/* position does not change 'cause we didn't advance it before */
 			break;
 
@@ -191,14 +189,14 @@ ExecLimit(Limit *node)
 			 * Advancing after having backed off window start: simply
 			 * re-return the last tuple fetched from the subplan.
 			 */
-			slot = limitstate->subSlot;
-			limitstate->lstate = LIMIT_INWINDOW;
+			slot = node->subSlot;
+			node->lstate = LIMIT_INWINDOW;
 			/* position does not change 'cause we didn't change it before */
 			break;
 
 		default:
 			elog(ERROR, "ExecLimit: impossible state %d",
-				 (int) limitstate->lstate);
+				 (int) node->lstate);
 			slot = NULL;		/* keep compiler quiet */
 			break;
 	}
@@ -220,55 +218,54 @@ ExecLimit(Limit *node)
  * This is also a handy place to reset the current-position state info.
  */
 static void
-recompute_limits(Limit *node)
+recompute_limits(LimitState *node)
 {
-	LimitState *limitstate = node->limitstate;
-	ExprContext *econtext = limitstate->cstate.cs_ExprContext;
+	ExprContext *econtext = node->ps.ps_ExprContext;
 	bool		isNull;
 
 	if (node->limitOffset)
 	{
-		limitstate->offset =
+		node->offset =
 			DatumGetInt32(ExecEvalExprSwitchContext(node->limitOffset,
 													econtext,
 													&isNull,
 													NULL));
 		/* Interpret NULL offset as no offset */
 		if (isNull)
-			limitstate->offset = 0;
-		else if (limitstate->offset < 0)
-			limitstate->offset = 0;
+			node->offset = 0;
+		else if (node->offset < 0)
+			node->offset = 0;
 	}
 	else
 	{
 		/* No OFFSET supplied */
-		limitstate->offset = 0;
+		node->offset = 0;
 	}
 
 	if (node->limitCount)
 	{
-		limitstate->noCount = false;
-		limitstate->count =
+		node->noCount = false;
+		node->count =
 			DatumGetInt32(ExecEvalExprSwitchContext(node->limitCount,
 													econtext,
 													&isNull,
 													NULL));
 		/* Interpret NULL count as no count (LIMIT ALL) */
 		if (isNull)
-			limitstate->noCount = true;
-		else if (limitstate->count < 0)
-			limitstate->count = 0;
+			node->noCount = true;
+		else if (node->count < 0)
+			node->count = 0;
 	}
 	else
 	{
 		/* No COUNT supplied */
-		limitstate->count = 0;
-		limitstate->noCount = true;
+		node->count = 0;
+		node->noCount = true;
 	}
 
 	/* Reset position to start-of-scan */
-	limitstate->position = 0;
-	limitstate->subSlot = NULL;
+	node->position = 0;
+	node->subSlot = NULL;
 }
 
 /* ----------------------------------------------------------------
@@ -278,22 +275,19 @@ recompute_limits(Limit *node)
  *		the node's subplan.
  * ----------------------------------------------------------------
  */
-bool							/* return: initialization status */
-ExecInitLimit(Limit *node, EState *estate, Plan *parent)
+LimitState *
+ExecInitLimit(Limit *node, EState *estate)
 {
 	LimitState *limitstate;
 	Plan	   *outerPlan;
 
 	/*
-	 * assign execution state to node
-	 */
-	node->plan.state = estate;
-
-	/*
-	 * create new LimitState for node
+	 * create state structure
 	 */
 	limitstate = makeNode(LimitState);
-	node->limitstate = limitstate;
+	limitstate->ps.plan = (Plan *) node;
+	limitstate->ps.state = estate;
+
 	limitstate->lstate = LIMIT_INITIAL;
 
 	/*
@@ -302,29 +296,37 @@ ExecInitLimit(Limit *node, EState *estate, Plan *parent)
 	 * Limit nodes never call ExecQual or ExecProject, but they need an
 	 * exprcontext anyway to evaluate the limit/offset parameters in.
 	 */
-	ExecAssignExprContext(estate, &limitstate->cstate);
+	ExecAssignExprContext(estate, &limitstate->ps);
+
+	/*
+	 * initialize child expressions
+	 */
+	limitstate->limitOffset = ExecInitExpr(node->limitOffset,
+										   (PlanState *) limitstate);
+	limitstate->limitCount = ExecInitExpr(node->limitCount,
+										  (PlanState *) limitstate);
 
 #define LIMIT_NSLOTS 1
 
 	/*
 	 * Tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &limitstate->cstate);
+	ExecInitResultTupleSlot(estate, &limitstate->ps);
 
 	/*
 	 * then initialize outer plan
 	 */
-	outerPlan = outerPlan((Plan *) node);
-	ExecInitNode(outerPlan, estate, (Plan *) node);
+	outerPlan = outerPlan(node);
+	outerPlanState(limitstate) = ExecInitNode(outerPlan, estate);
 
 	/*
 	 * limit nodes do no projections, so initialize projection info for
 	 * this node appropriately
 	 */
-	ExecAssignResultTypeFromOuterPlan((Plan *) node, &limitstate->cstate);
-	limitstate->cstate.cs_ProjInfo = NULL;
+	ExecAssignResultTypeFromOuterPlan(&limitstate->ps);
+	limitstate->ps.ps_ProjInfo = NULL;
 
-	return TRUE;
+	return limitstate;
 }
 
 int
@@ -343,33 +345,29 @@ ExecCountSlotsLimit(Limit *node)
  * ----------------------------------------------------------------
  */
 void
-ExecEndLimit(Limit *node)
+ExecEndLimit(LimitState *node)
 {
-	LimitState *limitstate = node->limitstate;
+	ExecFreeExprContext(&node->ps);
 
-	ExecFreeExprContext(&limitstate->cstate);
-
-	ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
+	ExecEndNode(outerPlanState(node));
 
 	/* clean up tuple table */
-	ExecClearTuple(limitstate->cstate.cs_ResultTupleSlot);
+	ExecClearTuple(node->ps.ps_ResultTupleSlot);
 }
 
 
 void
-ExecReScanLimit(Limit *node, ExprContext *exprCtxt, Plan *parent)
+ExecReScanLimit(LimitState *node, ExprContext *exprCtxt)
 {
-	LimitState *limitstate = node->limitstate;
-
 	/* resetting lstate will force offset/limit recalculation */
-	limitstate->lstate = LIMIT_INITIAL;
+	node->lstate = LIMIT_INITIAL;
 
-	ExecClearTuple(limitstate->cstate.cs_ResultTupleSlot);
+	ExecClearTuple(node->ps.ps_ResultTupleSlot);
 
 	/*
 	 * if chgParam of subnode is not null then plan will be re-scanned by
 	 * first ExecProcNode.
 	 */
-	if (((Plan *) node)->lefttree->chgParam == NULL)
-		ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
+	if (((PlanState *) node)->lefttree->chgParam == NULL)
+		ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
 }
diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c
index 30eb9a285b6..cf7ca89f4a9 100644
--- a/src/backend/executor/nodeMaterial.c
+++ b/src/backend/executor/nodeMaterial.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.38 2002/06/20 20:29:28 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.39 2002/12/05 15:50:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -43,10 +43,9 @@
  * ----------------------------------------------------------------
  */
 TupleTableSlot *				/* result tuple from subplan */
-ExecMaterial(Material *node)
+ExecMaterial(MaterialState *node)
 {
 	EState	   *estate;
-	MaterialState *matstate;
 	ScanDirection dir;
 	Tuplestorestate *tuplestorestate;
 	HeapTuple	heapTuple;
@@ -56,10 +55,9 @@ ExecMaterial(Material *node)
 	/*
 	 * get state info from node
 	 */
-	matstate = node->matstate;
-	estate = node->plan.state;
+	estate = node->ss.ps.state;
 	dir = estate->es_direction;
-	tuplestorestate = (Tuplestorestate *) matstate->tuplestorestate;
+	tuplestorestate = (Tuplestorestate *) node->tuplestorestate;
 
 	/*
 	 * If first time through, read all tuples from outer plan and pass
@@ -69,7 +67,7 @@ ExecMaterial(Material *node)
 
 	if (tuplestorestate == NULL)
 	{
-		Plan	   *outerNode;
+		PlanState  *outerNode;
 
 		/*
 		 * Want to scan subplan in the forward direction while creating
@@ -84,16 +82,16 @@ ExecMaterial(Material *node)
 		tuplestorestate = tuplestore_begin_heap(true,	/* randomAccess */
 												SortMem);
 
-		matstate->tuplestorestate = (void *) tuplestorestate;
+		node->tuplestorestate = (void *) tuplestorestate;
 
 		/*
 		 * Scan the subplan and feed all the tuples to tuplestore.
 		 */
-		outerNode = outerPlan((Plan *) node);
+		outerNode = outerPlanState(node);
 
 		for (;;)
 		{
-			slot = ExecProcNode(outerNode, (Plan *) node);
+			slot = ExecProcNode(outerNode);
 
 			if (TupIsNull(slot))
 				break;
@@ -117,7 +115,7 @@ ExecMaterial(Material *node)
 	 * Get the first or next tuple from tuplestore. Returns NULL if no
 	 * more tuples.
 	 */
-	slot = (TupleTableSlot *) matstate->csstate.cstate.cs_ResultTupleSlot;
+	slot = (TupleTableSlot *) node->ss.ps.ps_ResultTupleSlot;
 	heapTuple = tuplestore_getheaptuple(tuplestorestate,
 										ScanDirectionIsForward(dir),
 										&should_free);
@@ -129,23 +127,20 @@ ExecMaterial(Material *node)
  *		ExecInitMaterial
  * ----------------------------------------------------------------
  */
-bool							/* initialization status */
-ExecInitMaterial(Material *node, EState *estate, Plan *parent)
+MaterialState *
+ExecInitMaterial(Material *node, EState *estate)
 {
 	MaterialState *matstate;
 	Plan	   *outerPlan;
 
-	/*
-	 * assign the node's execution state
-	 */
-	node->plan.state = estate;
-
 	/*
 	 * create state structure
 	 */
 	matstate = makeNode(MaterialState);
+	matstate->ss.ps.plan = (Plan *) node;
+	matstate->ss.ps.state = estate;
+
 	matstate->tuplestorestate = NULL;
-	node->matstate = matstate;
 
 	/*
 	 * Miscellaneous initialization
@@ -161,24 +156,24 @@ ExecInitMaterial(Material *node, EState *estate, Plan *parent)
 	 *
 	 * material nodes only return tuples from their materialized relation.
 	 */
-	ExecInitResultTupleSlot(estate, &matstate->csstate.cstate);
-	ExecInitScanTupleSlot(estate, &matstate->csstate);
+	ExecInitResultTupleSlot(estate, &matstate->ss.ps);
+	ExecInitScanTupleSlot(estate, &matstate->ss);
 
 	/*
 	 * initializes child nodes
 	 */
-	outerPlan = outerPlan((Plan *) node);
-	ExecInitNode(outerPlan, estate, (Plan *) node);
+	outerPlan = outerPlan(node);
+	outerPlanState(matstate) = ExecInitNode(outerPlan, estate);
 
 	/*
 	 * initialize tuple type.  no need to initialize projection info
 	 * because this node doesn't do projections.
 	 */
-	ExecAssignResultTypeFromOuterPlan((Plan *) node, &matstate->csstate.cstate);
-	ExecAssignScanTypeFromOuterPlan((Plan *) node, &matstate->csstate);
-	matstate->csstate.cstate.cs_ProjInfo = NULL;
+	ExecAssignResultTypeFromOuterPlan(&matstate->ss.ps);
+	ExecAssignScanTypeFromOuterPlan(&matstate->ss);
+	matstate->ss.ps.ps_ProjInfo = NULL;
 
-	return TRUE;
+	return matstate;
 }
 
 int
@@ -194,33 +189,24 @@ ExecCountSlotsMaterial(Material *node)
  * ----------------------------------------------------------------
  */
 void
-ExecEndMaterial(Material *node)
+ExecEndMaterial(MaterialState *node)
 {
-	MaterialState *matstate;
-	Plan	   *outerPlan;
-
 	/*
-	 * get info from the material state
+	 * clean out the tuple table
 	 */
-	matstate = node->matstate;
+	ExecClearTuple(node->ss.ss_ScanTupleSlot);
 
 	/*
 	 * shut down the subplan
 	 */
-	outerPlan = outerPlan((Plan *) node);
-	ExecEndNode(outerPlan, (Plan *) node);
-
-	/*
-	 * clean out the tuple table
-	 */
-	ExecClearTuple(matstate->csstate.css_ScanTupleSlot);
+	ExecEndNode(outerPlanState(node));
 
 	/*
 	 * Release tuplestore resources
 	 */
-	if (matstate->tuplestorestate != NULL)
-		tuplestore_end((Tuplestorestate *) matstate->tuplestorestate);
-	matstate->tuplestorestate = NULL;
+	if (node->tuplestorestate != NULL)
+		tuplestore_end((Tuplestorestate *) node->tuplestorestate);
+	node->tuplestorestate = NULL;
 }
 
 /* ----------------------------------------------------------------
@@ -230,17 +216,15 @@ ExecEndMaterial(Material *node)
  * ----------------------------------------------------------------
  */
 void
-ExecMaterialMarkPos(Material *node)
+ExecMaterialMarkPos(MaterialState *node)
 {
-	MaterialState *matstate = node->matstate;
-
 	/*
 	 * if we haven't materialized yet, just return.
 	 */
-	if (!matstate->tuplestorestate)
+	if (!node->tuplestorestate)
 		return;
 
-	tuplestore_markpos((Tuplestorestate *) matstate->tuplestorestate);
+	tuplestore_markpos((Tuplestorestate *) node->tuplestorestate);
 }
 
 /* ----------------------------------------------------------------
@@ -250,20 +234,18 @@ ExecMaterialMarkPos(Material *node)
  * ----------------------------------------------------------------
  */
 void
-ExecMaterialRestrPos(Material *node)
+ExecMaterialRestrPos(MaterialState *node)
 {
-	MaterialState *matstate = node->matstate;
-
 	/*
 	 * if we haven't materialized yet, just return.
 	 */
-	if (!matstate->tuplestorestate)
+	if (!node->tuplestorestate)
 		return;
 
 	/*
 	 * restore the scan to the previously marked position
 	 */
-	tuplestore_restorepos((Tuplestorestate *) matstate->tuplestorestate);
+	tuplestore_restorepos((Tuplestorestate *) node->tuplestorestate);
 }
 
 /* ----------------------------------------------------------------
@@ -273,19 +255,17 @@ ExecMaterialRestrPos(Material *node)
  * ----------------------------------------------------------------
  */
 void
-ExecMaterialReScan(Material *node, ExprContext *exprCtxt, Plan *parent)
+ExecMaterialReScan(MaterialState *node, ExprContext *exprCtxt)
 {
-	MaterialState *matstate = node->matstate;
-
 	/*
 	 * If we haven't materialized yet, just return. If outerplan' chgParam
 	 * is not NULL then it will be re-scanned by ExecProcNode, else - no
 	 * reason to re-scan it at all.
 	 */
-	if (!matstate->tuplestorestate)
+	if (!node->tuplestorestate)
 		return;
 
-	ExecClearTuple(matstate->csstate.cstate.cs_ResultTupleSlot);
+	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
 
 	/*
 	 * If subnode is to be rescanned then we forget previous stored
@@ -293,11 +273,11 @@ ExecMaterialReScan(Material *node, ExprContext *exprCtxt, Plan *parent)
 	 *
 	 * Otherwise we can just rewind and rescan the stored output.
 	 */
-	if (((Plan *) node)->lefttree->chgParam != NULL)
+	if (((PlanState *) node)->lefttree->chgParam != NULL)
 	{
-		tuplestore_end((Tuplestorestate *) matstate->tuplestorestate);
-		matstate->tuplestorestate = NULL;
+		tuplestore_end((Tuplestorestate *) node->tuplestorestate);
+		node->tuplestorestate = NULL;
 	}
 	else
-		tuplestore_rescan((Tuplestorestate *) matstate->tuplestorestate);
+		tuplestore_rescan((Tuplestorestate *) node->tuplestorestate);
 }
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 4237618b0e5..171738dd349 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.51 2002/09/04 20:31:18 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.52 2002/12/05 15:50:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -340,10 +340,9 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
-ExecMergeJoin(MergeJoin *node)
+ExecMergeJoin(MergeJoinState *node)
 {
 	EState	   *estate;
-	MergeJoinState *mergestate;
 	ScanDirection direction;
 	List	   *innerSkipQual;
 	List	   *outerSkipQual;
@@ -352,9 +351,9 @@ ExecMergeJoin(MergeJoin *node)
 	List	   *otherqual;
 	bool		qualResult;
 	bool		compareResult;
-	Plan	   *innerPlan;
+	PlanState   *innerPlan;
 	TupleTableSlot *innerTupleSlot;
-	Plan	   *outerPlan;
+	PlanState   *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	ExprContext *econtext;
 	bool		doFillOuter;
@@ -363,17 +362,16 @@ ExecMergeJoin(MergeJoin *node)
 	/*
 	 * get information from node
 	 */
-	mergestate = node->mergestate;
-	estate = node->join.plan.state;
+	estate = node->js.ps.state;
 	direction = estate->es_direction;
-	innerPlan = innerPlan((Plan *) node);
-	outerPlan = outerPlan((Plan *) node);
-	econtext = mergestate->jstate.cs_ExprContext;
+	innerPlan = innerPlanState(node);
+	outerPlan = outerPlanState(node);
+	econtext = node->js.ps.ps_ExprContext;
 	mergeclauses = node->mergeclauses;
-	joinqual = node->join.joinqual;
-	otherqual = node->join.plan.qual;
+	joinqual = node->js.joinqual;
+	otherqual = node->js.ps.qual;
 
-	switch (node->join.jointype)
+	switch (node->js.jointype)
 	{
 		case JOIN_INNER:
 			doFillOuter = false;
@@ -393,7 +391,7 @@ ExecMergeJoin(MergeJoin *node)
 			break;
 		default:
 			elog(ERROR, "ExecMergeJoin: unsupported join type %d",
-				 (int) node->join.jointype);
+				 (int) node->js.jointype);
 			doFillOuter = false;	/* keep compiler quiet */
 			doFillInner = false;
 			break;
@@ -401,13 +399,13 @@ ExecMergeJoin(MergeJoin *node)
 
 	if (ScanDirectionIsForward(direction))
 	{
-		outerSkipQual = mergestate->mj_OuterSkipQual;
-		innerSkipQual = mergestate->mj_InnerSkipQual;
+		outerSkipQual = node->mj_OuterSkipQual;
+		innerSkipQual = node->mj_InnerSkipQual;
 	}
 	else
 	{
-		outerSkipQual = mergestate->mj_InnerSkipQual;
-		innerSkipQual = mergestate->mj_OuterSkipQual;
+		outerSkipQual = node->mj_InnerSkipQual;
+		innerSkipQual = node->mj_OuterSkipQual;
 	}
 
 	/*
@@ -415,16 +413,16 @@ ExecMergeJoin(MergeJoin *node)
 	 * join tuple (because there is a function-returning-set in the
 	 * projection expressions).  If so, try to project another one.
 	 */
-	if (mergestate->jstate.cs_TupFromTlist)
+	if (node->js.ps.ps_TupFromTlist)
 	{
 		TupleTableSlot *result;
 		ExprDoneCond isDone;
 
-		result = ExecProject(mergestate->jstate.cs_ProjInfo, &isDone);
+		result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
 		if (isDone == ExprMultipleResult)
 			return result;
 		/* Done with that source tuple... */
-		mergestate->jstate.cs_TupFromTlist = false;
+		node->js.ps.ps_TupFromTlist = false;
 	}
 
 	/*
@@ -444,9 +442,9 @@ ExecMergeJoin(MergeJoin *node)
 		 * Note: The join states are highlighted with 32-* comments for
 		 * improved readability.
 		 */
-		MJ_dump(mergestate);
+		MJ_dump(node);
 
-		switch (mergestate->mj_JoinState)
+		switch (node->mj_JoinState)
 		{
 				/*
 				 * EXEC_MJ_INITIALIZE means that this is the first time
@@ -459,8 +457,8 @@ ExecMergeJoin(MergeJoin *node)
 			case EXEC_MJ_INITIALIZE:
 				MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n");
 
-				outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
-				mergestate->mj_OuterTupleSlot = outerTupleSlot;
+				outerTupleSlot = ExecProcNode(outerPlan);
+				node->mj_OuterTupleSlot = outerTupleSlot;
 				if (TupIsNull(outerTupleSlot))
 				{
 					MJ_printf("ExecMergeJoin: outer subplan is empty\n");
@@ -471,16 +469,16 @@ ExecMergeJoin(MergeJoin *node)
 						 * inner tuples.  We set MatchedInner = true to
 						 * force the ENDOUTER state to advance inner.
 						 */
-						mergestate->mj_JoinState = EXEC_MJ_ENDOUTER;
-						mergestate->mj_MatchedInner = true;
+						node->mj_JoinState = EXEC_MJ_ENDOUTER;
+						node->mj_MatchedInner = true;
 						break;
 					}
 					/* Otherwise we're done. */
 					return NULL;
 				}
 
-				innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
-				mergestate->mj_InnerTupleSlot = innerTupleSlot;
+				innerTupleSlot = ExecProcNode(innerPlan);
+				node->mj_InnerTupleSlot = innerTupleSlot;
 				if (TupIsNull(innerTupleSlot))
 				{
 					MJ_printf("ExecMergeJoin: inner subplan is empty\n");
@@ -493,8 +491,8 @@ ExecMergeJoin(MergeJoin *node)
 						 * state to emit this tuple before advancing
 						 * outer.
 						 */
-						mergestate->mj_JoinState = EXEC_MJ_ENDINNER;
-						mergestate->mj_MatchedOuter = false;
+						node->mj_JoinState = EXEC_MJ_ENDINNER;
+						node->mj_MatchedOuter = false;
 						break;
 					}
 					/* Otherwise we're done. */
@@ -505,7 +503,7 @@ ExecMergeJoin(MergeJoin *node)
 				 * OK, we have the initial tuples.	Begin by skipping
 				 * unmatched inner tuples.
 				 */
-				mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_BEGIN;
+				node->mj_JoinState = EXEC_MJ_SKIPINNER_BEGIN;
 				break;
 
 				/*
@@ -519,9 +517,9 @@ ExecMergeJoin(MergeJoin *node)
 
 				ExecMarkPos(innerPlan);
 
-				MarkInnerTuple(mergestate->mj_InnerTupleSlot, mergestate);
+				MarkInnerTuple(node->mj_InnerTupleSlot, node);
 
-				mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
+				node->mj_JoinState = EXEC_MJ_JOINTEST;
 				break;
 
 				/*
@@ -538,18 +536,18 @@ ExecMergeJoin(MergeJoin *node)
 
 				ResetExprContext(econtext);
 
-				outerTupleSlot = mergestate->mj_OuterTupleSlot;
+				outerTupleSlot = node->mj_OuterTupleSlot;
 				econtext->ecxt_outertuple = outerTupleSlot;
-				innerTupleSlot = mergestate->mj_InnerTupleSlot;
+				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
 				qualResult = ExecQual(mergeclauses, econtext, false);
 				MJ_DEBUG_QUAL(mergeclauses, qualResult);
 
 				if (qualResult)
-					mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
+					node->mj_JoinState = EXEC_MJ_JOINTUPLES;
 				else
-					mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
+					node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 				break;
 
 				/*
@@ -560,7 +558,7 @@ ExecMergeJoin(MergeJoin *node)
 			case EXEC_MJ_JOINTUPLES:
 				MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
 
-				mergestate->mj_JoinState = EXEC_MJ_NEXTINNER;
+				node->mj_JoinState = EXEC_MJ_NEXTINNER;
 
 				/*
 				 * Check the extra qual conditions to see if we actually
@@ -582,8 +580,8 @@ ExecMergeJoin(MergeJoin *node)
 
 				if (qualResult)
 				{
-					mergestate->mj_MatchedOuter = true;
-					mergestate->mj_MatchedInner = true;
+					node->mj_MatchedOuter = true;
+					node->mj_MatchedInner = true;
 
 					qualResult = (otherqual == NIL ||
 								  ExecQual(otherqual, econtext, false));
@@ -601,12 +599,12 @@ ExecMergeJoin(MergeJoin *node)
 
 						MJ_printf("ExecMergeJoin: returning tuple\n");
 
-						result = ExecProject(mergestate->jstate.cs_ProjInfo,
+						result = ExecProject(node->js.ps.ps_ProjInfo,
 											 &isDone);
 
 						if (isDone != ExprEndResult)
 						{
-							mergestate->jstate.cs_TupFromTlist =
+							node->js.ps.ps_TupFromTlist =
 								(isDone == ExprMultipleResult);
 							return result;
 						}
@@ -625,20 +623,20 @@ ExecMergeJoin(MergeJoin *node)
 			case EXEC_MJ_NEXTINNER:
 				MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n");
 
-				if (doFillInner && !mergestate->mj_MatchedInner)
+				if (doFillInner && !node->mj_MatchedInner)
 				{
 					/*
 					 * Generate a fake join tuple with nulls for the outer
 					 * tuple, and return it if it passes the non-join
 					 * quals.
 					 */
-					mergestate->mj_MatchedInner = true; /* do it only once */
+					node->mj_MatchedInner = true; /* do it only once */
 
 					ResetExprContext(econtext);
 
-					outerTupleSlot = mergestate->mj_NullOuterTupleSlot;
+					outerTupleSlot = node->mj_NullOuterTupleSlot;
 					econtext->ecxt_outertuple = outerTupleSlot;
-					innerTupleSlot = mergestate->mj_InnerTupleSlot;
+					innerTupleSlot = node->mj_InnerTupleSlot;
 					econtext->ecxt_innertuple = innerTupleSlot;
 
 					if (ExecQual(otherqual, econtext, false))
@@ -653,12 +651,12 @@ ExecMergeJoin(MergeJoin *node)
 
 						MJ_printf("ExecMergeJoin: returning fill tuple\n");
 
-						result = ExecProject(mergestate->jstate.cs_ProjInfo,
+						result = ExecProject(node->js.ps.ps_ProjInfo,
 											 &isDone);
 
 						if (isDone != ExprEndResult)
 						{
-							mergestate->jstate.cs_TupFromTlist =
+							node->js.ps.ps_TupFromTlist =
 								(isDone == ExprMultipleResult);
 							return result;
 						}
@@ -668,15 +666,15 @@ ExecMergeJoin(MergeJoin *node)
 				/*
 				 * now we get the next inner tuple, if any
 				 */
-				innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
-				mergestate->mj_InnerTupleSlot = innerTupleSlot;
+				innerTupleSlot = ExecProcNode(innerPlan);
+				node->mj_InnerTupleSlot = innerTupleSlot;
 				MJ_DEBUG_PROC_NODE(innerTupleSlot);
-				mergestate->mj_MatchedInner = false;
+				node->mj_MatchedInner = false;
 
 				if (TupIsNull(innerTupleSlot))
-					mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
+					node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 				else
-					mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
+					node->mj_JoinState = EXEC_MJ_JOINTEST;
 				break;
 
 				/*-------------------------------------------
@@ -701,20 +699,20 @@ ExecMergeJoin(MergeJoin *node)
 			case EXEC_MJ_NEXTOUTER:
 				MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n");
 
-				if (doFillOuter && !mergestate->mj_MatchedOuter)
+				if (doFillOuter && !node->mj_MatchedOuter)
 				{
 					/*
 					 * Generate a fake join tuple with nulls for the inner
 					 * tuple, and return it if it passes the non-join
 					 * quals.
 					 */
-					mergestate->mj_MatchedOuter = true; /* do it only once */
+					node->mj_MatchedOuter = true; /* do it only once */
 
 					ResetExprContext(econtext);
 
-					outerTupleSlot = mergestate->mj_OuterTupleSlot;
+					outerTupleSlot = node->mj_OuterTupleSlot;
 					econtext->ecxt_outertuple = outerTupleSlot;
-					innerTupleSlot = mergestate->mj_NullInnerTupleSlot;
+					innerTupleSlot = node->mj_NullInnerTupleSlot;
 					econtext->ecxt_innertuple = innerTupleSlot;
 
 					if (ExecQual(otherqual, econtext, false))
@@ -729,12 +727,12 @@ ExecMergeJoin(MergeJoin *node)
 
 						MJ_printf("ExecMergeJoin: returning fill tuple\n");
 
-						result = ExecProject(mergestate->jstate.cs_ProjInfo,
+						result = ExecProject(node->js.ps.ps_ProjInfo,
 											 &isDone);
 
 						if (isDone != ExprEndResult)
 						{
-							mergestate->jstate.cs_TupFromTlist =
+							node->js.ps.ps_TupFromTlist =
 								(isDone == ExprMultipleResult);
 							return result;
 						}
@@ -744,10 +742,10 @@ ExecMergeJoin(MergeJoin *node)
 				/*
 				 * now we get the next outer tuple, if any
 				 */
-				outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
-				mergestate->mj_OuterTupleSlot = outerTupleSlot;
+				outerTupleSlot = ExecProcNode(outerPlan);
+				node->mj_OuterTupleSlot = outerTupleSlot;
 				MJ_DEBUG_PROC_NODE(outerTupleSlot);
-				mergestate->mj_MatchedOuter = false;
+				node->mj_MatchedOuter = false;
 
 				/*
 				 * if the outer tuple is null then we are done with the
@@ -756,21 +754,21 @@ ExecMergeJoin(MergeJoin *node)
 				if (TupIsNull(outerTupleSlot))
 				{
 					MJ_printf("ExecMergeJoin: end of outer subplan\n");
-					innerTupleSlot = mergestate->mj_InnerTupleSlot;
+					innerTupleSlot = node->mj_InnerTupleSlot;
 					if (doFillInner && !TupIsNull(innerTupleSlot))
 					{
 						/*
 						 * Need to emit right-join tuples for remaining
 						 * inner tuples.
 						 */
-						mergestate->mj_JoinState = EXEC_MJ_ENDOUTER;
+						node->mj_JoinState = EXEC_MJ_ENDOUTER;
 						break;
 					}
 					/* Otherwise we're done. */
 					return NULL;
 				}
 
-				mergestate->mj_JoinState = EXEC_MJ_TESTOUTER;
+				node->mj_JoinState = EXEC_MJ_TESTOUTER;
 				break;
 
 				/*--------------------------------------------------------
@@ -816,9 +814,9 @@ ExecMergeJoin(MergeJoin *node)
 				 */
 				ResetExprContext(econtext);
 
-				outerTupleSlot = mergestate->mj_OuterTupleSlot;
+				outerTupleSlot = node->mj_OuterTupleSlot;
 				econtext->ecxt_outertuple = outerTupleSlot;
-				innerTupleSlot = mergestate->mj_MarkedTupleSlot;
+				innerTupleSlot = node->mj_MarkedTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
 				qualResult = ExecQual(mergeclauses, econtext, false);
@@ -843,7 +841,7 @@ ExecMergeJoin(MergeJoin *node)
 					 * the extra joinquals.
 					 */
 					ExecRestrPos(innerPlan);
-					mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
+					node->mj_JoinState = EXEC_MJ_JOINTUPLES;
 				}
 				else
 				{
@@ -862,7 +860,7 @@ ExecMergeJoin(MergeJoin *node)
 					 *	larger than our marked inner tuples.  So we're done.
 					 * ----------------
 					 */
-					innerTupleSlot = mergestate->mj_InnerTupleSlot;
+					innerTupleSlot = node->mj_InnerTupleSlot;
 					if (TupIsNull(innerTupleSlot))
 					{
 						if (doFillOuter)
@@ -871,7 +869,7 @@ ExecMergeJoin(MergeJoin *node)
 							 * Need to emit left-join tuples for remaining
 							 * outer tuples.
 							 */
-							mergestate->mj_JoinState = EXEC_MJ_ENDINNER;
+							node->mj_JoinState = EXEC_MJ_ENDINNER;
 							break;
 						}
 						/* Otherwise we're done. */
@@ -879,7 +877,7 @@ ExecMergeJoin(MergeJoin *node)
 					}
 
 					/* continue on to skip outer tuples */
-					mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN;
+					node->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN;
 				}
 				break;
 
@@ -913,9 +911,9 @@ ExecMergeJoin(MergeJoin *node)
 				 */
 				ResetExprContext(econtext);
 
-				outerTupleSlot = mergestate->mj_OuterTupleSlot;
+				outerTupleSlot = node->mj_OuterTupleSlot;
 				econtext->ecxt_outertuple = outerTupleSlot;
-				innerTupleSlot = mergestate->mj_InnerTupleSlot;
+				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
 				qualResult = ExecQual(mergeclauses, econtext, false);
@@ -925,13 +923,13 @@ ExecMergeJoin(MergeJoin *node)
 				{
 					ExecMarkPos(innerPlan);
 
-					MarkInnerTuple(innerTupleSlot, mergestate);
+					MarkInnerTuple(innerTupleSlot, node);
 
-					mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
+					node->mj_JoinState = EXEC_MJ_JOINTUPLES;
 					break;
 				}
 
-				mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST;
+				node->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST;
 				break;
 
 			case EXEC_MJ_SKIPOUTER_TEST:
@@ -940,9 +938,9 @@ ExecMergeJoin(MergeJoin *node)
 				/*
 				 * ok, now test the skip qualification
 				 */
-				outerTupleSlot = mergestate->mj_OuterTupleSlot;
+				outerTupleSlot = node->mj_OuterTupleSlot;
 				econtext->ecxt_outertuple = outerTupleSlot;
-				innerTupleSlot = mergestate->mj_InnerTupleSlot;
+				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
 				compareResult = MergeCompare(mergeclauses,
@@ -957,7 +955,7 @@ ExecMergeJoin(MergeJoin *node)
 				 */
 				if (compareResult)
 				{
-					mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_ADVANCE;
+					node->mj_JoinState = EXEC_MJ_SKIPOUTER_ADVANCE;
 					break;
 				}
 
@@ -973,9 +971,9 @@ ExecMergeJoin(MergeJoin *node)
 				MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
 
 				if (compareResult)
-					mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_BEGIN;
+					node->mj_JoinState = EXEC_MJ_SKIPINNER_BEGIN;
 				else
-					mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
+					node->mj_JoinState = EXEC_MJ_JOINMARK;
 				break;
 
 				/*
@@ -985,20 +983,20 @@ ExecMergeJoin(MergeJoin *node)
 			case EXEC_MJ_SKIPOUTER_ADVANCE:
 				MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_ADVANCE\n");
 
-				if (doFillOuter && !mergestate->mj_MatchedOuter)
+				if (doFillOuter && !node->mj_MatchedOuter)
 				{
 					/*
 					 * Generate a fake join tuple with nulls for the inner
 					 * tuple, and return it if it passes the non-join
 					 * quals.
 					 */
-					mergestate->mj_MatchedOuter = true; /* do it only once */
+					node->mj_MatchedOuter = true; /* do it only once */
 
 					ResetExprContext(econtext);
 
-					outerTupleSlot = mergestate->mj_OuterTupleSlot;
+					outerTupleSlot = node->mj_OuterTupleSlot;
 					econtext->ecxt_outertuple = outerTupleSlot;
-					innerTupleSlot = mergestate->mj_NullInnerTupleSlot;
+					innerTupleSlot = node->mj_NullInnerTupleSlot;
 					econtext->ecxt_innertuple = innerTupleSlot;
 
 					if (ExecQual(otherqual, econtext, false))
@@ -1013,12 +1011,12 @@ ExecMergeJoin(MergeJoin *node)
 
 						MJ_printf("ExecMergeJoin: returning fill tuple\n");
 
-						result = ExecProject(mergestate->jstate.cs_ProjInfo,
+						result = ExecProject(node->js.ps.ps_ProjInfo,
 											 &isDone);
 
 						if (isDone != ExprEndResult)
 						{
-							mergestate->jstate.cs_TupFromTlist =
+							node->js.ps.ps_TupFromTlist =
 								(isDone == ExprMultipleResult);
 							return result;
 						}
@@ -1028,10 +1026,10 @@ ExecMergeJoin(MergeJoin *node)
 				/*
 				 * now we get the next outer tuple, if any
 				 */
-				outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
-				mergestate->mj_OuterTupleSlot = outerTupleSlot;
+				outerTupleSlot = ExecProcNode(outerPlan);
+				node->mj_OuterTupleSlot = outerTupleSlot;
 				MJ_DEBUG_PROC_NODE(outerTupleSlot);
-				mergestate->mj_MatchedOuter = false;
+				node->mj_MatchedOuter = false;
 
 				/*
 				 * if the outer tuple is null then we are done with the
@@ -1040,14 +1038,14 @@ ExecMergeJoin(MergeJoin *node)
 				if (TupIsNull(outerTupleSlot))
 				{
 					MJ_printf("ExecMergeJoin: end of outer subplan\n");
-					innerTupleSlot = mergestate->mj_InnerTupleSlot;
+					innerTupleSlot = node->mj_InnerTupleSlot;
 					if (doFillInner && !TupIsNull(innerTupleSlot))
 					{
 						/*
 						 * Need to emit right-join tuples for remaining
 						 * inner tuples.
 						 */
-						mergestate->mj_JoinState = EXEC_MJ_ENDOUTER;
+						node->mj_JoinState = EXEC_MJ_ENDOUTER;
 						break;
 					}
 					/* Otherwise we're done. */
@@ -1057,7 +1055,7 @@ ExecMergeJoin(MergeJoin *node)
 				/*
 				 * otherwise test the new tuple against the skip qual.
 				 */
-				mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST;
+				node->mj_JoinState = EXEC_MJ_SKIPOUTER_TEST;
 				break;
 
 				/*-----------------------------------------------------------
@@ -1090,9 +1088,9 @@ ExecMergeJoin(MergeJoin *node)
 				 */
 				ResetExprContext(econtext);
 
-				outerTupleSlot = mergestate->mj_OuterTupleSlot;
+				outerTupleSlot = node->mj_OuterTupleSlot;
 				econtext->ecxt_outertuple = outerTupleSlot;
-				innerTupleSlot = mergestate->mj_InnerTupleSlot;
+				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
 				qualResult = ExecQual(mergeclauses, econtext, false);
@@ -1102,13 +1100,13 @@ ExecMergeJoin(MergeJoin *node)
 				{
 					ExecMarkPos(innerPlan);
 
-					MarkInnerTuple(innerTupleSlot, mergestate);
+					MarkInnerTuple(innerTupleSlot, node);
 
-					mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
+					node->mj_JoinState = EXEC_MJ_JOINTUPLES;
 					break;
 				}
 
-				mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_TEST;
+				node->mj_JoinState = EXEC_MJ_SKIPINNER_TEST;
 				break;
 
 			case EXEC_MJ_SKIPINNER_TEST:
@@ -1117,9 +1115,9 @@ ExecMergeJoin(MergeJoin *node)
 				/*
 				 * ok, now test the skip qualification
 				 */
-				outerTupleSlot = mergestate->mj_OuterTupleSlot;
+				outerTupleSlot = node->mj_OuterTupleSlot;
 				econtext->ecxt_outertuple = outerTupleSlot;
-				innerTupleSlot = mergestate->mj_InnerTupleSlot;
+				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
 				compareResult = MergeCompare(mergeclauses,
@@ -1134,7 +1132,7 @@ ExecMergeJoin(MergeJoin *node)
 				 */
 				if (compareResult)
 				{
-					mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE;
+					node->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE;
 					break;
 				}
 
@@ -1150,9 +1148,9 @@ ExecMergeJoin(MergeJoin *node)
 				MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
 
 				if (compareResult)
-					mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN;
+					node->mj_JoinState = EXEC_MJ_SKIPOUTER_BEGIN;
 				else
-					mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
+					node->mj_JoinState = EXEC_MJ_JOINMARK;
 				break;
 
 				/*
@@ -1162,20 +1160,20 @@ ExecMergeJoin(MergeJoin *node)
 			case EXEC_MJ_SKIPINNER_ADVANCE:
 				MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_ADVANCE\n");
 
-				if (doFillInner && !mergestate->mj_MatchedInner)
+				if (doFillInner && !node->mj_MatchedInner)
 				{
 					/*
 					 * Generate a fake join tuple with nulls for the outer
 					 * tuple, and return it if it passes the non-join
 					 * quals.
 					 */
-					mergestate->mj_MatchedInner = true; /* do it only once */
+					node->mj_MatchedInner = true; /* do it only once */
 
 					ResetExprContext(econtext);
 
-					outerTupleSlot = mergestate->mj_NullOuterTupleSlot;
+					outerTupleSlot = node->mj_NullOuterTupleSlot;
 					econtext->ecxt_outertuple = outerTupleSlot;
-					innerTupleSlot = mergestate->mj_InnerTupleSlot;
+					innerTupleSlot = node->mj_InnerTupleSlot;
 					econtext->ecxt_innertuple = innerTupleSlot;
 
 					if (ExecQual(otherqual, econtext, false))
@@ -1190,12 +1188,12 @@ ExecMergeJoin(MergeJoin *node)
 
 						MJ_printf("ExecMergeJoin: returning fill tuple\n");
 
-						result = ExecProject(mergestate->jstate.cs_ProjInfo,
+						result = ExecProject(node->js.ps.ps_ProjInfo,
 											 &isDone);
 
 						if (isDone != ExprEndResult)
 						{
-							mergestate->jstate.cs_TupFromTlist =
+							node->js.ps.ps_TupFromTlist =
 								(isDone == ExprMultipleResult);
 							return result;
 						}
@@ -1205,10 +1203,10 @@ ExecMergeJoin(MergeJoin *node)
 				/*
 				 * now we get the next inner tuple, if any
 				 */
-				innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
-				mergestate->mj_InnerTupleSlot = innerTupleSlot;
+				innerTupleSlot = ExecProcNode(innerPlan);
+				node->mj_InnerTupleSlot = innerTupleSlot;
 				MJ_DEBUG_PROC_NODE(innerTupleSlot);
-				mergestate->mj_MatchedInner = false;
+				node->mj_MatchedInner = false;
 
 				/*
 				 * if the inner tuple is null then we are done with the
@@ -1217,14 +1215,14 @@ ExecMergeJoin(MergeJoin *node)
 				if (TupIsNull(innerTupleSlot))
 				{
 					MJ_printf("ExecMergeJoin: end of inner subplan\n");
-					outerTupleSlot = mergestate->mj_OuterTupleSlot;
+					outerTupleSlot = node->mj_OuterTupleSlot;
 					if (doFillOuter && !TupIsNull(outerTupleSlot))
 					{
 						/*
 						 * Need to emit left-join tuples for remaining
 						 * outer tuples.
 						 */
-						mergestate->mj_JoinState = EXEC_MJ_ENDINNER;
+						node->mj_JoinState = EXEC_MJ_ENDINNER;
 						break;
 					}
 					/* Otherwise we're done. */
@@ -1234,7 +1232,7 @@ ExecMergeJoin(MergeJoin *node)
 				/*
 				 * otherwise test the new tuple against the skip qual.
 				 */
-				mergestate->mj_JoinState = EXEC_MJ_SKIPINNER_TEST;
+				node->mj_JoinState = EXEC_MJ_SKIPINNER_TEST;
 				break;
 
 				/*
@@ -1247,20 +1245,20 @@ ExecMergeJoin(MergeJoin *node)
 
 				Assert(doFillInner);
 
-				if (!mergestate->mj_MatchedInner)
+				if (!node->mj_MatchedInner)
 				{
 					/*
 					 * Generate a fake join tuple with nulls for the outer
 					 * tuple, and return it if it passes the non-join
 					 * quals.
 					 */
-					mergestate->mj_MatchedInner = true; /* do it only once */
+					node->mj_MatchedInner = true; /* do it only once */
 
 					ResetExprContext(econtext);
 
-					outerTupleSlot = mergestate->mj_NullOuterTupleSlot;
+					outerTupleSlot = node->mj_NullOuterTupleSlot;
 					econtext->ecxt_outertuple = outerTupleSlot;
-					innerTupleSlot = mergestate->mj_InnerTupleSlot;
+					innerTupleSlot = node->mj_InnerTupleSlot;
 					econtext->ecxt_innertuple = innerTupleSlot;
 
 					if (ExecQual(otherqual, econtext, false))
@@ -1275,12 +1273,12 @@ ExecMergeJoin(MergeJoin *node)
 
 						MJ_printf("ExecMergeJoin: returning fill tuple\n");
 
-						result = ExecProject(mergestate->jstate.cs_ProjInfo,
+						result = ExecProject(node->js.ps.ps_ProjInfo,
 											 &isDone);
 
 						if (isDone != ExprEndResult)
 						{
-							mergestate->jstate.cs_TupFromTlist =
+							node->js.ps.ps_TupFromTlist =
 								(isDone == ExprMultipleResult);
 							return result;
 						}
@@ -1290,10 +1288,10 @@ ExecMergeJoin(MergeJoin *node)
 				/*
 				 * now we get the next inner tuple, if any
 				 */
-				innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
-				mergestate->mj_InnerTupleSlot = innerTupleSlot;
+				innerTupleSlot = ExecProcNode(innerPlan);
+				node->mj_InnerTupleSlot = innerTupleSlot;
 				MJ_DEBUG_PROC_NODE(innerTupleSlot);
-				mergestate->mj_MatchedInner = false;
+				node->mj_MatchedInner = false;
 
 				if (TupIsNull(innerTupleSlot))
 				{
@@ -1314,20 +1312,20 @@ ExecMergeJoin(MergeJoin *node)
 
 				Assert(doFillOuter);
 
-				if (!mergestate->mj_MatchedOuter)
+				if (!node->mj_MatchedOuter)
 				{
 					/*
 					 * Generate a fake join tuple with nulls for the inner
 					 * tuple, and return it if it passes the non-join
 					 * quals.
 					 */
-					mergestate->mj_MatchedOuter = true; /* do it only once */
+					node->mj_MatchedOuter = true; /* do it only once */
 
 					ResetExprContext(econtext);
 
-					outerTupleSlot = mergestate->mj_OuterTupleSlot;
+					outerTupleSlot = node->mj_OuterTupleSlot;
 					econtext->ecxt_outertuple = outerTupleSlot;
-					innerTupleSlot = mergestate->mj_NullInnerTupleSlot;
+					innerTupleSlot = node->mj_NullInnerTupleSlot;
 					econtext->ecxt_innertuple = innerTupleSlot;
 
 					if (ExecQual(otherqual, econtext, false))
@@ -1342,12 +1340,12 @@ ExecMergeJoin(MergeJoin *node)
 
 						MJ_printf("ExecMergeJoin: returning fill tuple\n");
 
-						result = ExecProject(mergestate->jstate.cs_ProjInfo,
+						result = ExecProject(node->js.ps.ps_ProjInfo,
 											 &isDone);
 
 						if (isDone != ExprEndResult)
 						{
-							mergestate->jstate.cs_TupFromTlist =
+							node->js.ps.ps_TupFromTlist =
 								(isDone == ExprMultipleResult);
 							return result;
 						}
@@ -1357,10 +1355,10 @@ ExecMergeJoin(MergeJoin *node)
 				/*
 				 * now we get the next outer tuple, if any
 				 */
-				outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
-				mergestate->mj_OuterTupleSlot = outerTupleSlot;
+				outerTupleSlot = ExecProcNode(outerPlan);
+				node->mj_OuterTupleSlot = outerTupleSlot;
 				MJ_DEBUG_PROC_NODE(outerTupleSlot);
-				mergestate->mj_MatchedOuter = false;
+				node->mj_MatchedOuter = false;
 
 				if (TupIsNull(outerTupleSlot))
 				{
@@ -1377,7 +1375,7 @@ ExecMergeJoin(MergeJoin *node)
 				 */
 			default:
 				elog(WARNING, "ExecMergeJoin: invalid join state %d, aborting",
-					 mergestate->mj_JoinState);
+					 node->mj_JoinState);
 				return NULL;
 		}
 	}
@@ -1385,14 +1383,10 @@ ExecMergeJoin(MergeJoin *node)
 
 /* ----------------------------------------------------------------
  *		ExecInitMergeJoin
- *
- * old comments
- *		Creates the run-time state information for the node and
- *		sets the relation id to contain relevant decriptors.
  * ----------------------------------------------------------------
  */
-bool
-ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
+MergeJoinState *
+ExecInitMergeJoin(MergeJoin *node, EState *estate)
 {
 	MergeJoinState *mergestate;
 
@@ -1400,40 +1394,52 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
 			   "initializing node");
 
 	/*
-	 * assign the node's execution state and get the range table and
-	 * direction from it
-	 */
-	node->join.plan.state = estate;
-
-	/*
-	 * create new merge state for node
+	 * create state structure
 	 */
 	mergestate = makeNode(MergeJoinState);
-	node->mergestate = mergestate;
+	mergestate->js.ps.plan = (Plan *) node;
+	mergestate->js.ps.state = estate;
 
 	/*
 	 * Miscellaneous initialization
 	 *
 	 * create expression context for node
 	 */
-	ExecAssignExprContext(estate, &mergestate->jstate);
+	ExecAssignExprContext(estate, &mergestate->js.ps);
+
+	/*
+	 * initialize child expressions
+	 */
+	mergestate->js.ps.targetlist = (List *)
+		ExecInitExpr((Node *) node->join.plan.targetlist,
+					 (PlanState *) mergestate);
+	mergestate->js.ps.qual = (List *)
+		ExecInitExpr((Node *) node->join.plan.qual,
+					 (PlanState *) mergestate);
+	mergestate->js.jointype = node->join.jointype;
+	mergestate->js.joinqual = (List *)
+		ExecInitExpr((Node *) node->join.joinqual,
+					 (PlanState *) mergestate);
+	mergestate->mergeclauses = (List *)
+		ExecInitExpr((Node *) node->mergeclauses,
+					 (PlanState *) mergestate);
 
 	/*
-	 * initialize subplans
+	 * initialize child nodes
 	 */
-	ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
-	ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
+	outerPlanState(mergestate) = ExecInitNode(outerPlan(node), estate);
+	innerPlanState(mergestate) = ExecInitNode(innerPlan(node), estate);
 
 #define MERGEJOIN_NSLOTS 4
 
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &mergestate->jstate);
+	ExecInitResultTupleSlot(estate, &mergestate->js.ps);
 
 	mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate);
 	ExecSetSlotDescriptor(mergestate->mj_MarkedTupleSlot,
-						  ExecGetTupType(innerPlan((Plan *) node)),
+						  ExecGetTupType(innerPlanState(mergestate)),
 						  false);
 
 	switch (node->join.jointype)
@@ -1443,12 +1449,12 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
 		case JOIN_LEFT:
 			mergestate->mj_NullInnerTupleSlot =
 				ExecInitNullTupleSlot(estate,
-							   ExecGetTupType(innerPlan((Plan *) node)));
+							   ExecGetTupType(innerPlanState(mergestate)));
 			break;
 		case JOIN_RIGHT:
 			mergestate->mj_NullOuterTupleSlot =
 				ExecInitNullTupleSlot(estate,
-							   ExecGetTupType(outerPlan((Plan *) node)));
+							   ExecGetTupType(outerPlanState(mergestate)));
 
 			/*
 			 * Can't handle right or full join with non-nil extra
@@ -1460,10 +1466,10 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
 		case JOIN_FULL:
 			mergestate->mj_NullOuterTupleSlot =
 				ExecInitNullTupleSlot(estate,
-							   ExecGetTupType(outerPlan((Plan *) node)));
+							   ExecGetTupType(outerPlanState(mergestate)));
 			mergestate->mj_NullInnerTupleSlot =
 				ExecInitNullTupleSlot(estate,
-							   ExecGetTupType(innerPlan((Plan *) node)));
+							   ExecGetTupType(innerPlanState(mergestate)));
 
 			/*
 			 * Can't handle right or full join with non-nil extra
@@ -1480,8 +1486,8 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
 	/*
 	 * initialize tuple type and projection info
 	 */
-	ExecAssignResultTypeFromTL((Plan *) node, &mergestate->jstate);
-	ExecAssignProjectionInfo((Plan *) node, &mergestate->jstate);
+	ExecAssignResultTypeFromTL(&mergestate->js.ps);
+	ExecAssignProjectionInfo(&mergestate->js.ps);
 
 	/*
 	 * form merge skip qualifications
@@ -1500,7 +1506,7 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
 	 * initialize join state
 	 */
 	mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
-	mergestate->jstate.cs_TupFromTlist = false;
+	mergestate->js.ps.ps_TupFromTlist = false;
 	mergestate->mj_MatchedOuter = false;
 	mergestate->mj_MatchedInner = false;
 	mergestate->mj_OuterTupleSlot = NULL;
@@ -1512,7 +1518,7 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
 	MJ1_printf("ExecInitMergeJoin: %s\n",
 			   "node initialized");
 
-	return TRUE;
+	return mergestate;
 }
 
 int
@@ -1531,65 +1537,52 @@ ExecCountSlotsMergeJoin(MergeJoin *node)
  * ----------------------------------------------------------------
  */
 void
-ExecEndMergeJoin(MergeJoin *node)
+ExecEndMergeJoin(MergeJoinState *node)
 {
-	MergeJoinState *mergestate;
-
 	MJ1_printf("ExecEndMergeJoin: %s\n",
 			   "ending node processing");
 
-	/*
-	 * get state information from the node
-	 */
-	mergestate = node->mergestate;
-
 	/*
 	 * Free the projection info and the scan attribute info
-	 *
-	 * Note: we don't ExecFreeResultType(mergestate) because the rule manager
-	 * depends on the tupType returned by ExecMain().  So for now, this is
-	 * freed at end-transaction time.  -cim 6/2/91
 	 */
-	ExecFreeProjectionInfo(&mergestate->jstate);
-	ExecFreeExprContext(&mergestate->jstate);
+	ExecFreeProjectionInfo(&node->js.ps);
+	ExecFreeExprContext(&node->js.ps);
 
 	/*
 	 * shut down the subplans
 	 */
-	ExecEndNode((Plan *) innerPlan((Plan *) node), (Plan *) node);
-	ExecEndNode((Plan *) outerPlan((Plan *) node), (Plan *) node);
+	ExecEndNode(innerPlanState(node));
+	ExecEndNode(outerPlanState(node));
 
 	/*
 	 * clean out the tuple table
 	 */
-	ExecClearTuple(mergestate->jstate.cs_ResultTupleSlot);
-	ExecClearTuple(mergestate->mj_MarkedTupleSlot);
+	ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
+	ExecClearTuple(node->mj_MarkedTupleSlot);
 
 	MJ1_printf("ExecEndMergeJoin: %s\n",
 			   "node processing ended");
 }
 
 void
-ExecReScanMergeJoin(MergeJoin *node, ExprContext *exprCtxt, Plan *parent)
+ExecReScanMergeJoin(MergeJoinState *node, ExprContext *exprCtxt)
 {
-	MergeJoinState *mergestate = node->mergestate;
-
-	ExecClearTuple(mergestate->mj_MarkedTupleSlot);
+	ExecClearTuple(node->mj_MarkedTupleSlot);
 
-	mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
-	mergestate->jstate.cs_TupFromTlist = false;
-	mergestate->mj_MatchedOuter = false;
-	mergestate->mj_MatchedInner = false;
-	mergestate->mj_OuterTupleSlot = NULL;
-	mergestate->mj_InnerTupleSlot = NULL;
+	node->mj_JoinState = EXEC_MJ_INITIALIZE;
+	node->js.ps.ps_TupFromTlist = false;
+	node->mj_MatchedOuter = false;
+	node->mj_MatchedInner = false;
+	node->mj_OuterTupleSlot = NULL;
+	node->mj_InnerTupleSlot = NULL;
 
 	/*
 	 * if chgParam of subnodes is not null then plans will be re-scanned
 	 * by first ExecProcNode.
 	 */
-	if (((Plan *) node)->lefttree->chgParam == NULL)
-		ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
-	if (((Plan *) node)->righttree->chgParam == NULL)
-		ExecReScan(((Plan *) node)->righttree, exprCtxt, (Plan *) node);
+	if (((PlanState *) node)->lefttree->chgParam == NULL)
+		ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
+	if (((PlanState *) node)->righttree->chgParam == NULL)
+		ExecReScan(((PlanState *) node)->righttree, exprCtxt);
 
 }
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 2bd26938fcc..ae2061aa969 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.26 2002/06/20 20:29:28 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.27 2002/12/05 15:50:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -57,11 +57,10 @@
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
-ExecNestLoop(NestLoop *node)
+ExecNestLoop(NestLoopState *node)
 {
-	NestLoopState *nlstate;
-	Plan	   *innerPlan;
-	Plan	   *outerPlan;
+	PlanState  *innerPlan;
+	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
 	List	   *joinqual;
@@ -73,17 +72,16 @@ ExecNestLoop(NestLoop *node)
 	 */
 	ENL1_printf("getting info from node");
 
-	nlstate = node->nlstate;
-	joinqual = node->join.joinqual;
-	otherqual = node->join.plan.qual;
-	outerPlan = outerPlan((Plan *) node);
-	innerPlan = innerPlan((Plan *) node);
-	econtext = nlstate->jstate.cs_ExprContext;
+	joinqual = node->js.joinqual;
+	otherqual = node->js.ps.qual;
+	outerPlan = outerPlanState(node);
+	innerPlan = innerPlanState(node);
+	econtext = node->js.ps.ps_ExprContext;
 
 	/*
 	 * get the current outer tuple
 	 */
-	outerTupleSlot = nlstate->jstate.cs_OuterTupleSlot;
+	outerTupleSlot = node->js.ps.ps_OuterTupleSlot;
 	econtext->ecxt_outertuple = outerTupleSlot;
 
 	/*
@@ -91,16 +89,16 @@ ExecNestLoop(NestLoop *node)
 	 * join tuple (because there is a function-returning-set in the
 	 * projection expressions).  If so, try to project another one.
 	 */
-	if (nlstate->jstate.cs_TupFromTlist)
+	if (node->js.ps.ps_TupFromTlist)
 	{
 		TupleTableSlot *result;
 		ExprDoneCond isDone;
 
-		result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
+		result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
 		if (isDone == ExprMultipleResult)
 			return result;
 		/* Done with that source tuple... */
-		nlstate->jstate.cs_TupFromTlist = false;
+		node->js.ps.ps_TupFromTlist = false;
 	}
 
 	/*
@@ -122,10 +120,10 @@ ExecNestLoop(NestLoop *node)
 		 * If we don't have an outer tuple, get the next one and reset the
 		 * inner scan.
 		 */
-		if (nlstate->nl_NeedNewOuter)
+		if (node->nl_NeedNewOuter)
 		{
 			ENL1_printf("getting new outer tuple");
-			outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
+			outerTupleSlot = ExecProcNode(outerPlan);
 
 			/*
 			 * if there are no more outer tuples, then the join is
@@ -138,10 +136,10 @@ ExecNestLoop(NestLoop *node)
 			}
 
 			ENL1_printf("saving new outer tuple information");
-			nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot;
+			node->js.ps.ps_OuterTupleSlot = outerTupleSlot;
 			econtext->ecxt_outertuple = outerTupleSlot;
-			nlstate->nl_NeedNewOuter = false;
-			nlstate->nl_MatchedOuter = false;
+			node->nl_NeedNewOuter = false;
+			node->nl_MatchedOuter = false;
 
 			/*
 			 * now rescan the inner plan
@@ -153,7 +151,7 @@ ExecNestLoop(NestLoop *node)
 			 * outer tuple (e.g. in index scans), that's why we pass our
 			 * expr context.
 			 */
-			ExecReScan(innerPlan, econtext, (Plan *) node);
+			ExecReScan(innerPlan, econtext);
 		}
 
 		/*
@@ -161,17 +159,17 @@ ExecNestLoop(NestLoop *node)
 		 */
 		ENL1_printf("getting new inner tuple");
 
-		innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
+		innerTupleSlot = ExecProcNode(innerPlan);
 		econtext->ecxt_innertuple = innerTupleSlot;
 
 		if (TupIsNull(innerTupleSlot))
 		{
 			ENL1_printf("no inner tuple, need new outer tuple");
 
-			nlstate->nl_NeedNewOuter = true;
+			node->nl_NeedNewOuter = true;
 
-			if (!nlstate->nl_MatchedOuter &&
-				node->join.jointype == JOIN_LEFT)
+			if (!node->nl_MatchedOuter &&
+				node->js.jointype == JOIN_LEFT)
 			{
 				/*
 				 * We are doing an outer join and there were no join
@@ -179,7 +177,7 @@ ExecNestLoop(NestLoop *node)
 				 * tuple with nulls for the inner tuple, and return it if
 				 * it passes the non-join quals.
 				 */
-				econtext->ecxt_innertuple = nlstate->nl_NullInnerTupleSlot;
+				econtext->ecxt_innertuple = node->nl_NullInnerTupleSlot;
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
@@ -195,11 +193,11 @@ ExecNestLoop(NestLoop *node)
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
+					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
 
 					if (isDone != ExprEndResult)
 					{
-						nlstate->jstate.cs_TupFromTlist =
+						node->js.ps.ps_TupFromTlist =
 							(isDone == ExprMultipleResult);
 						return result;
 					}
@@ -224,7 +222,7 @@ ExecNestLoop(NestLoop *node)
 
 		if (ExecQual(joinqual, econtext, false))
 		{
-			nlstate->nl_MatchedOuter = true;
+			node->nl_MatchedOuter = true;
 
 			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
 			{
@@ -238,11 +236,11 @@ ExecNestLoop(NestLoop *node)
 
 				ENL1_printf("qualification succeeded, projecting tuple");
 
-				result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
+				result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
 
 				if (isDone != ExprEndResult)
 				{
-					nlstate->jstate.cs_TupFromTlist =
+					node->js.ps.ps_TupFromTlist =
 						(isDone == ExprMultipleResult);
 					return result;
 				}
@@ -260,14 +258,10 @@ ExecNestLoop(NestLoop *node)
 
 /* ----------------------------------------------------------------
  *		ExecInitNestLoop
- *
- *		Creates the run-time state information for the nestloop node
- *		produced by the planner and initailizes inner and outer relations
- *		(child nodes).
  * ----------------------------------------------------------------
  */
-bool
-ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
+NestLoopState *
+ExecInitNestLoop(NestLoop *node, EState *estate)
 {
 	NestLoopState *nlstate;
 
@@ -275,35 +269,45 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
 			   "initializing node");
 
 	/*
-	 * assign execution state to node
-	 */
-	node->join.plan.state = estate;
-
-	/*
-	 * create new nest loop state
+	 * create state structure
 	 */
 	nlstate = makeNode(NestLoopState);
-	node->nlstate = nlstate;
+	nlstate->js.ps.plan = (Plan *) node;
+	nlstate->js.ps.state = estate;
 
 	/*
 	 * Miscellaneous initialization
 	 *
 	 * create expression context for node
 	 */
-	ExecAssignExprContext(estate, &nlstate->jstate);
+	ExecAssignExprContext(estate, &nlstate->js.ps);
 
 	/*
-	 * now initialize children
+	 * initialize child expressions
 	 */
-	ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
-	ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
+	nlstate->js.ps.targetlist = (List *)
+		ExecInitExpr((Node *) node->join.plan.targetlist,
+					 (PlanState *) nlstate);
+	nlstate->js.ps.qual = (List *)
+		ExecInitExpr((Node *) node->join.plan.qual,
+					 (PlanState *) nlstate);
+	nlstate->js.jointype = node->join.jointype;
+	nlstate->js.joinqual = (List *)
+		ExecInitExpr((Node *) node->join.joinqual,
+					 (PlanState *) nlstate);
+
+	/*
+	 * initialize child nodes
+	 */
+	outerPlanState(nlstate) = ExecInitNode(outerPlan(node), estate);
+	innerPlanState(nlstate) = ExecInitNode(innerPlan(node), estate);
 
 #define NESTLOOP_NSLOTS 2
 
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &nlstate->jstate);
+	ExecInitResultTupleSlot(estate, &nlstate->js.ps);
 
 	switch (node->join.jointype)
 	{
@@ -312,7 +316,7 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
 		case JOIN_LEFT:
 			nlstate->nl_NullInnerTupleSlot =
 				ExecInitNullTupleSlot(estate,
-							   ExecGetTupType(innerPlan((Plan *) node)));
+							   ExecGetTupType(innerPlanState(nlstate)));
 			break;
 		default:
 			elog(ERROR, "ExecInitNestLoop: unsupported join type %d",
@@ -322,20 +326,21 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
 	/*
 	 * initialize tuple type and projection info
 	 */
-	ExecAssignResultTypeFromTL((Plan *) node, &nlstate->jstate);
-	ExecAssignProjectionInfo((Plan *) node, &nlstate->jstate);
+	ExecAssignResultTypeFromTL(&nlstate->js.ps);
+	ExecAssignProjectionInfo(&nlstate->js.ps);
 
 	/*
 	 * finally, wipe the current outer tuple clean.
 	 */
-	nlstate->jstate.cs_OuterTupleSlot = NULL;
-	nlstate->jstate.cs_TupFromTlist = false;
+	nlstate->js.ps.ps_OuterTupleSlot = NULL;
+	nlstate->js.ps.ps_TupFromTlist = false;
 	nlstate->nl_NeedNewOuter = true;
 	nlstate->nl_MatchedOuter = false;
 
 	NL1_printf("ExecInitNestLoop: %s\n",
 			   "node initialized");
-	return TRUE;
+
+	return nlstate;
 }
 
 int
@@ -353,38 +358,27 @@ ExecCountSlotsNestLoop(NestLoop *node)
  * ----------------------------------------------------------------
  */
 void
-ExecEndNestLoop(NestLoop *node)
+ExecEndNestLoop(NestLoopState *node)
 {
-	NestLoopState *nlstate;
-
 	NL1_printf("ExecEndNestLoop: %s\n",
 			   "ending node processing");
 
-	/*
-	 * get info from the node
-	 */
-	nlstate = node->nlstate;
-
 	/*
 	 * Free the projection info
-	 *
-	 * Note: we don't ExecFreeResultType(nlstate) because the rule manager
-	 * depends on the tupType returned by ExecMain().  So for now, this is
-	 * freed at end-transaction time.  -cim 6/2/91
 	 */
-	ExecFreeProjectionInfo(&nlstate->jstate);
-	ExecFreeExprContext(&nlstate->jstate);
+	ExecFreeProjectionInfo(&node->js.ps);
+	ExecFreeExprContext(&node->js.ps);
 
 	/*
 	 * close down subplans
 	 */
-	ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
-	ExecEndNode(innerPlan((Plan *) node), (Plan *) node);
+	ExecEndNode(outerPlanState(node));
+	ExecEndNode(innerPlanState(node));
 
 	/*
 	 * clean out the tuple table
 	 */
-	ExecClearTuple(nlstate->jstate.cs_ResultTupleSlot);
+	ExecClearTuple(node->js.ps.ps_ResultTupleSlot);
 
 	NL1_printf("ExecEndNestLoop: %s\n",
 			   "node processing ended");
@@ -395,10 +389,9 @@ ExecEndNestLoop(NestLoop *node)
  * ----------------------------------------------------------------
  */
 void
-ExecReScanNestLoop(NestLoop *node, ExprContext *exprCtxt, Plan *parent)
+ExecReScanNestLoop(NestLoopState *node, ExprContext *exprCtxt)
 {
-	NestLoopState *nlstate = node->nlstate;
-	Plan	   *outerPlan = outerPlan((Plan *) node);
+	PlanState   *outerPlan = outerPlanState(node);
 
 	/*
 	 * If outerPlan->chgParam is not null then plan will be automatically
@@ -408,11 +401,11 @@ ExecReScanNestLoop(NestLoop *node, ExprContext *exprCtxt, Plan *parent)
 	 * run-time keys...
 	 */
 	if (outerPlan->chgParam == NULL)
-		ExecReScan(outerPlan, exprCtxt, (Plan *) node);
+		ExecReScan(outerPlan, exprCtxt);
 
 	/* let outerPlan to free its result tuple ... */
-	nlstate->jstate.cs_OuterTupleSlot = NULL;
-	nlstate->jstate.cs_TupFromTlist = false;
-	nlstate->nl_NeedNewOuter = true;
-	nlstate->nl_MatchedOuter = false;
+	node->js.ps.ps_OuterTupleSlot = NULL;
+	node->js.ps.ps_TupFromTlist = false;
+	node->nl_NeedNewOuter = true;
+	node->nl_MatchedOuter = false;
 }
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index d9d1608a81d..f14fd01c424 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -34,7 +34,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.21 2002/06/20 20:29:28 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.22 2002/12/05 15:50:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -60,34 +60,29 @@
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
-ExecResult(Result *node)
+ExecResult(ResultState *node)
 {
-	ResultState *resstate;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *resultSlot;
-	Plan	   *outerPlan;
+	PlanState   *outerPlan;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 
-	/*
-	 * initialize the result node's state
-	 */
-	resstate = node->resstate;
-	econtext = resstate->cstate.cs_ExprContext;
+	econtext = node->ps.ps_ExprContext;
 
 	/*
 	 * check constant qualifications like (2 > 1), if not already done
 	 */
-	if (resstate->rs_checkqual)
+	if (node->rs_checkqual)
 	{
 		bool		qualResult = ExecQual((List *) node->resconstantqual,
 										  econtext,
 										  false);
 
-		resstate->rs_checkqual = false;
-		if (qualResult == false)
+		node->rs_checkqual = false;
+		if (!qualResult)
 		{
-			resstate->rs_done = true;
+			node->rs_done = true;
 			return NULL;
 		}
 	}
@@ -97,13 +92,13 @@ ExecResult(Result *node)
 	 * scan tuple (because there is a function-returning-set in the
 	 * projection expressions).  If so, try to project another one.
 	 */
-	if (resstate->cstate.cs_TupFromTlist)
+	if (node->ps.ps_TupFromTlist)
 	{
-		resultSlot = ExecProject(resstate->cstate.cs_ProjInfo, &isDone);
+		resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone);
 		if (isDone == ExprMultipleResult)
 			return resultSlot;
 		/* Done with that source tuple... */
-		resstate->cstate.cs_TupFromTlist = false;
+		node->ps.ps_TupFromTlist = false;
 	}
 
 	/*
@@ -119,9 +114,9 @@ ExecResult(Result *node)
 	 * called, OR that we failed the constant qual check. Either way, now
 	 * we are through.
 	 */
-	while (!resstate->rs_done)
+	while (!node->rs_done)
 	{
-		outerPlan = outerPlan(node);
+		outerPlan = outerPlanState(node);
 
 		if (outerPlan != NULL)
 		{
@@ -129,12 +124,12 @@ ExecResult(Result *node)
 			 * retrieve tuples from the outer plan until there are no
 			 * more.
 			 */
-			outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
+			outerTupleSlot = ExecProcNode(outerPlan);
 
 			if (TupIsNull(outerTupleSlot))
 				return NULL;
 
-			resstate->cstate.cs_OuterTupleSlot = outerTupleSlot;
+			node->ps.ps_OuterTupleSlot = outerTupleSlot;
 
 			/*
 			 * XXX gross hack. use outer tuple as scan tuple for
@@ -149,7 +144,7 @@ ExecResult(Result *node)
 			 * if we don't have an outer plan, then we are just generating
 			 * the results from a constant target list.  Do it only once.
 			 */
-			resstate->rs_done = true;
+			node->rs_done = true;
 		}
 
 		/*
@@ -157,11 +152,11 @@ ExecResult(Result *node)
 		 * unless the projection produces an empty set, in which case we
 		 * must loop back to see if there are more outerPlan tuples.
 		 */
-		resultSlot = ExecProject(resstate->cstate.cs_ProjInfo, &isDone);
+		resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone);
 
 		if (isDone != ExprEndResult)
 		{
-			resstate->cstate.cs_TupFromTlist = (isDone == ExprMultipleResult);
+			node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult);
 			return resultSlot;
 		}
 	}
@@ -177,42 +172,51 @@ ExecResult(Result *node)
  *		(child nodes).
  * ----------------------------------------------------------------
  */
-bool
-ExecInitResult(Result *node, EState *estate, Plan *parent)
+ResultState *
+ExecInitResult(Result *node, EState *estate)
 {
 	ResultState *resstate;
 
 	/*
-	 * assign execution state to node
-	 */
-	node->plan.state = estate;
-
-	/*
-	 * create new ResultState for node
+	 * create state structure
 	 */
 	resstate = makeNode(ResultState);
+	resstate->ps.plan = (Plan *) node;
+	resstate->ps.state = estate;
+
 	resstate->rs_done = false;
 	resstate->rs_checkqual = (node->resconstantqual == NULL) ? false : true;
-	node->resstate = resstate;
 
 	/*
 	 * Miscellaneous initialization
 	 *
 	 * create expression context for node
 	 */
-	ExecAssignExprContext(estate, &resstate->cstate);
+	ExecAssignExprContext(estate, &resstate->ps);
 
 #define RESULT_NSLOTS 1
 
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &resstate->cstate);
+	ExecInitResultTupleSlot(estate, &resstate->ps);
 
 	/*
-	 * then initialize children
+	 * initialize child expressions
 	 */
-	ExecInitNode(outerPlan(node), estate, (Plan *) node);
+	resstate->ps.targetlist = (List *)
+		ExecInitExpr((Node *) node->plan.targetlist,
+					 (PlanState *) resstate);
+	resstate->ps.qual = (List *)
+		ExecInitExpr((Node *) node->plan.qual,
+					 (PlanState *) resstate);
+	resstate->resconstantqual = ExecInitExpr(node->resconstantqual,
+											 (PlanState *) resstate);
+
+	/*
+	 * initialize child nodes
+	 */
+	outerPlanState(resstate) = ExecInitNode(outerPlan(node), estate);
 
 	/*
 	 * we don't use inner plan
@@ -222,10 +226,10 @@ ExecInitResult(Result *node, EState *estate, Plan *parent)
 	/*
 	 * initialize tuple type and projection info
 	 */
-	ExecAssignResultTypeFromTL((Plan *) node, &resstate->cstate);
-	ExecAssignProjectionInfo((Plan *) node, &resstate->cstate);
+	ExecAssignResultTypeFromTL(&resstate->ps);
+	ExecAssignProjectionInfo(&resstate->ps);
 
-	return TRUE;
+	return resstate;
 }
 
 int
@@ -241,49 +245,37 @@ ExecCountSlotsResult(Result *node)
  * ----------------------------------------------------------------
  */
 void
-ExecEndResult(Result *node)
+ExecEndResult(ResultState *node)
 {
-	ResultState *resstate;
-
-	resstate = node->resstate;
-
 	/*
 	 * Free the projection info
-	 *
-	 * Note: we don't ExecFreeResultType(resstate) because the rule manager
-	 * depends on the tupType returned by ExecMain().  So for now, this is
-	 * freed at end-transaction time.  -cim 6/2/91
 	 */
-	ExecFreeProjectionInfo(&resstate->cstate);
-	ExecFreeExprContext(&resstate->cstate);
+	ExecFreeProjectionInfo(&node->ps);
+	ExecFreeExprContext(&node->ps);
 
 	/*
-	 * shut down subplans
+	 * clean out the tuple table
 	 */
-	ExecEndNode(outerPlan(node), (Plan *) node);
+	ExecClearTuple(node->ps.ps_ResultTupleSlot);
 
 	/*
-	 * clean out the tuple table
+	 * shut down subplans
 	 */
-	ExecClearTuple(resstate->cstate.cs_ResultTupleSlot);
-	pfree(resstate);
-	node->resstate = NULL;		/* XXX - new for us - er1p */
+	ExecEndNode(outerPlanState(node));
 }
 
 void
-ExecReScanResult(Result *node, ExprContext *exprCtxt, Plan *parent)
+ExecReScanResult(ResultState *node, ExprContext *exprCtxt)
 {
-	ResultState *resstate = node->resstate;
-
-	resstate->rs_done = false;
-	resstate->cstate.cs_TupFromTlist = false;
-	resstate->rs_checkqual = (node->resconstantqual == NULL) ? false : true;
+	node->rs_done = false;
+	node->ps.ps_TupFromTlist = false;
+	node->rs_checkqual = (node->resconstantqual == NULL) ? false : true;
 
 	/*
 	 * if chgParam of subnode is not null then plan will be re-scanned by
 	 * first ExecProcNode.
 	 */
-	if (((Plan *) node)->lefttree &&
-		((Plan *) node)->lefttree->chgParam == NULL)
-		ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
+	if (((PlanState *) node)->lefttree &&
+		((PlanState *) node)->lefttree->chgParam == NULL)
+		ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
 }
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index b6848d6c4b3..e19098fba23 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.38 2002/11/30 05:21:01 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.39 2002/12/05 15:50:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,9 +29,8 @@
 #include "executor/nodeSeqscan.h"
 #include "parser/parsetree.h"
 
-static Oid InitScanRelation(SeqScan *node, EState *estate,
-				 CommonScanState *scanstate);
-static TupleTableSlot *SeqNext(SeqScan *node);
+static void InitScanRelation(SeqScanState *node, EState *estate);
+static TupleTableSlot *SeqNext(SeqScanState *node);
 
 /* ----------------------------------------------------------------
  *						Scan Support
@@ -44,11 +43,11 @@ static TupleTableSlot *SeqNext(SeqScan *node);
  * ----------------------------------------------------------------
  */
 static TupleTableSlot *
-SeqNext(SeqScan *node)
+SeqNext(SeqScanState *node)
 {
 	HeapTuple	tuple;
 	HeapScanDesc scandesc;
-	CommonScanState *scanstate;
+	Index		scanrelid;
 	EState	   *estate;
 	ScanDirection direction;
 	TupleTableSlot *slot;
@@ -56,11 +55,11 @@ SeqNext(SeqScan *node)
 	/*
 	 * get information from the estate and scan state
 	 */
-	estate = node->plan.state;
-	scanstate = node->scanstate;
-	scandesc = scanstate->css_currentScanDesc;
+	estate = node->ps.state;
+	scandesc = node->ss_currentScanDesc;
+	scanrelid = ((SeqScan *) node->ps.plan)->scanrelid;
 	direction = estate->es_direction;
-	slot = scanstate->css_ScanTupleSlot;
+	slot = node->ss_ScanTupleSlot;
 
 	/*
 	 * Check if we are evaluating PlanQual for tuple of this relation.
@@ -69,13 +68,13 @@ SeqNext(SeqScan *node)
 	 * switching in Init/ReScan plan...
 	 */
 	if (estate->es_evTuple != NULL &&
-		estate->es_evTuple[node->scanrelid - 1] != NULL)
+		estate->es_evTuple[scanrelid - 1] != NULL)
 	{
 		ExecClearTuple(slot);
-		if (estate->es_evTupleNull[node->scanrelid - 1])
+		if (estate->es_evTupleNull[scanrelid - 1])
 			return slot;		/* return empty slot */
 
-		ExecStoreTuple(estate->es_evTuple[node->scanrelid - 1],
+		ExecStoreTuple(estate->es_evTuple[scanrelid - 1],
 					   slot, InvalidBuffer, false);
 
 		/*
@@ -85,7 +84,7 @@ SeqNext(SeqScan *node)
 		 */
 
 		/* Flag for the next call that no more tuples */
-		estate->es_evTupleNull[node->scanrelid - 1] = true;
+		estate->es_evTupleNull[scanrelid - 1] = true;
 		return (slot);
 	}
 
@@ -124,12 +123,12 @@ SeqNext(SeqScan *node)
  */
 
 TupleTableSlot *
-ExecSeqScan(SeqScan *node)
+ExecSeqScan(SeqScanState *node)
 {
 	/*
 	 * use SeqNext as access method
 	 */
-	return ExecScan(node, (ExecScanAccessMtd) SeqNext);
+	return ExecScan((ScanState *) node, (ExecScanAccessMtd) SeqNext);
 }
 
 /* ----------------------------------------------------------------
@@ -139,9 +138,8 @@ ExecSeqScan(SeqScan *node)
  *		subplans of scans.
  * ----------------------------------------------------------------
  */
-static Oid
-InitScanRelation(SeqScan *node, EState *estate,
-				 CommonScanState *scanstate)
+static void
+InitScanRelation(SeqScanState *node, EState *estate)
 {
 	Index		relid;
 	List	   *rangeTable;
@@ -156,7 +154,7 @@ InitScanRelation(SeqScan *node, EState *estate,
 	 *
 	 * We acquire AccessShareLock for the duration of the scan.
 	 */
-	relid = node->scanrelid;
+	relid = ((SeqScan *) node->ps.plan)->scanrelid;
 	rangeTable = estate->es_range_table;
 	rtentry = rt_fetch(relid, rangeTable);
 	reloid = rtentry->relid;
@@ -168,12 +166,10 @@ InitScanRelation(SeqScan *node, EState *estate,
 									 0,
 									 NULL);
 
-	scanstate->css_currentRelation = currentRelation;
-	scanstate->css_currentScanDesc = currentScanDesc;
+	node->ss_currentRelation = currentRelation;
+	node->ss_currentScanDesc = currentScanDesc;
 
-	ExecAssignScanType(scanstate, RelationGetDescr(currentRelation), false);
-
-	return reloid;
+	ExecAssignScanType(node, RelationGetDescr(currentRelation), false);
 }
 
 
@@ -181,59 +177,64 @@ InitScanRelation(SeqScan *node, EState *estate,
  *		ExecInitSeqScan
  * ----------------------------------------------------------------
  */
-bool
-ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
+SeqScanState *
+ExecInitSeqScan(SeqScan *node, EState *estate)
 {
-	CommonScanState *scanstate;
-	Oid			reloid;
+	SeqScanState *scanstate;
 
 	/*
 	 * Once upon a time it was possible to have an outerPlan of a SeqScan,
 	 * but not any more.
 	 */
-	Assert(outerPlan((Plan *) node) == NULL);
-	Assert(innerPlan((Plan *) node) == NULL);
-
-	/*
-	 * assign the node's execution state
-	 */
-	node->plan.state = estate;
+	Assert(outerPlan(node) == NULL);
+	Assert(innerPlan(node) == NULL);
 
 	/*
-	 * create new CommonScanState for node
+	 * create state structure
 	 */
-	scanstate = makeNode(CommonScanState);
-	node->scanstate = scanstate;
+	scanstate = makeNode(SeqScanState);
+	scanstate->ps.plan = (Plan *) node;
+	scanstate->ps.state = estate;
 
 	/*
 	 * Miscellaneous initialization
 	 *
 	 * create expression context for node
 	 */
-	ExecAssignExprContext(estate, &scanstate->cstate);
+	ExecAssignExprContext(estate, &scanstate->ps);
+
+	/*
+	 * initialize child expressions
+	 */
+	scanstate->ps.targetlist = (List *)
+		ExecInitExpr((Node *) node->plan.targetlist,
+					 (PlanState *) scanstate);
+	scanstate->ps.qual = (List *)
+		ExecInitExpr((Node *) node->plan.qual,
+					 (PlanState *) scanstate);
 
 #define SEQSCAN_NSLOTS 2
 
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &scanstate->cstate);
+	ExecInitResultTupleSlot(estate, &scanstate->ps);
 	ExecInitScanTupleSlot(estate, scanstate);
 
 	/*
 	 * initialize scan relation
 	 */
-	reloid = InitScanRelation(node, estate, scanstate);
+	InitScanRelation(scanstate, estate);
 
-	scanstate->cstate.cs_TupFromTlist = false;
+	scanstate->ps.ps_TupFromTlist = false;
 
 	/*
 	 * initialize tuple type
 	 */
-	ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
-	ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate);
+	ExecAssignResultTypeFromTL(&scanstate->ps);
+	ExecAssignProjectionInfo(&scanstate->ps);
 
-	return TRUE;
+	return scanstate;
 }
 
 int
@@ -251,34 +252,34 @@ ExecCountSlotsSeqScan(SeqScan *node)
  * ----------------------------------------------------------------
  */
 void
-ExecEndSeqScan(SeqScan *node)
+ExecEndSeqScan(SeqScanState *node)
 {
-	CommonScanState *scanstate;
 	Relation	relation;
 	HeapScanDesc scanDesc;
 
 	/*
 	 * get information from node
 	 */
-	scanstate = node->scanstate;
-	relation = scanstate->css_currentRelation;
-	scanDesc = scanstate->css_currentScanDesc;
+	relation = node->ss_currentRelation;
+	scanDesc = node->ss_currentScanDesc;
 
 	/*
 	 * Free the projection info and the scan attribute info
-	 *
-	 * Note: we don't ExecFreeResultType(scanstate) because the rule manager
-	 * depends on the tupType returned by ExecMain().  So for now, this is
-	 * freed at end-transaction time.  -cim 6/2/91
 	 */
-	ExecFreeProjectionInfo(&scanstate->cstate);
-	ExecFreeExprContext(&scanstate->cstate);
+	ExecFreeProjectionInfo(&node->ps);
+	ExecFreeExprContext(&node->ps);
 
 	/*
 	 * close heap scan
 	 */
 	heap_endscan(scanDesc);
 
+	/*
+	 * clean out the tuple table
+	 */
+	ExecClearTuple(node->ps.ps_ResultTupleSlot);
+	ExecClearTuple(node->ss_ScanTupleSlot);
+
 	/*
 	 * close the heap relation.
 	 *
@@ -288,12 +289,6 @@ ExecEndSeqScan(SeqScan *node)
 	 * locking, however.)
 	 */
 	heap_close(relation, NoLock);
-
-	/*
-	 * clean out the tuple table
-	 */
-	ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
-	ExecClearTuple(scanstate->css_ScanTupleSlot);
 }
 
 /* ----------------------------------------------------------------
@@ -308,24 +303,24 @@ ExecEndSeqScan(SeqScan *node)
  * ----------------------------------------------------------------
  */
 void
-ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan *parent)
+ExecSeqReScan(SeqScanState *node, ExprContext *exprCtxt)
 {
-	CommonScanState *scanstate;
 	EState	   *estate;
+	Index		scanrelid;
 	HeapScanDesc scan;
 
-	scanstate = node->scanstate;
-	estate = node->plan.state;
+	estate = node->ps.state;
+	scanrelid = ((SeqScan *) node->ps.plan)->scanrelid;
 
 	/* If this is re-scanning of PlanQual ... */
 	if (estate->es_evTuple != NULL &&
-		estate->es_evTuple[node->scanrelid - 1] != NULL)
+		estate->es_evTuple[scanrelid - 1] != NULL)
 	{
-		estate->es_evTupleNull[node->scanrelid - 1] = false;
+		estate->es_evTupleNull[scanrelid - 1] = false;
 		return;
 	}
 
-	scan = scanstate->css_currentScanDesc;
+	scan = node->ss_currentScanDesc;
 
 	heap_rescan(scan,			/* scan desc */
 				NULL);			/* new scan keys */
@@ -338,13 +333,11 @@ ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan *parent)
  * ----------------------------------------------------------------
  */
 void
-ExecSeqMarkPos(SeqScan *node)
+ExecSeqMarkPos(SeqScanState *node)
 {
-	CommonScanState *scanstate;
 	HeapScanDesc scan;
 
-	scanstate = node->scanstate;
-	scan = scanstate->css_currentScanDesc;
+	scan = node->ss_currentScanDesc;
 	heap_markpos(scan);
 }
 
@@ -355,12 +348,10 @@ ExecSeqMarkPos(SeqScan *node)
  * ----------------------------------------------------------------
  */
 void
-ExecSeqRestrPos(SeqScan *node)
+ExecSeqRestrPos(SeqScanState *node)
 {
-	CommonScanState *scanstate;
 	HeapScanDesc scan;
 
-	scanstate = node->scanstate;
-	scan = scanstate->css_currentScanDesc;
+	scan = node->ss_currentScanDesc;
 	heap_restrpos(scan);
 }
diff --git a/src/backend/executor/nodeSetOp.c b/src/backend/executor/nodeSetOp.c
index 3d1cf2c8efa..a81a4a29d91 100644
--- a/src/backend/executor/nodeSetOp.c
+++ b/src/backend/executor/nodeSetOp.c
@@ -21,7 +21,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSetOp.c,v 1.6 2002/06/20 20:29:28 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSetOp.c,v 1.7 2002/12/05 15:50:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,28 +44,27 @@
  * ----------------------------------------------------------------
  */
 TupleTableSlot *				/* return: a tuple or NULL */
-ExecSetOp(SetOp *node)
+ExecSetOp(SetOpState *node)
 {
-	SetOpState *setopstate;
+	SetOp	   *plannode = (SetOp *) node->ps.plan;
 	TupleTableSlot *resultTupleSlot;
-	Plan	   *outerPlan;
+	PlanState  *outerPlan;
 	TupleDesc	tupDesc;
 
 	/*
 	 * get information from the node
 	 */
-	setopstate = node->setopstate;
-	outerPlan = outerPlan((Plan *) node);
-	resultTupleSlot = setopstate->cstate.cs_ResultTupleSlot;
-	tupDesc = ExecGetResultType(&setopstate->cstate);
+	outerPlan = outerPlanState(node);
+	resultTupleSlot = node->ps.ps_ResultTupleSlot;
+	tupDesc = ExecGetResultType(&node->ps);
 
 	/*
 	 * If the previously-returned tuple needs to be returned more than
 	 * once, keep returning it.
 	 */
-	if (setopstate->numOutput > 0)
+	if (node->numOutput > 0)
 	{
-		setopstate->numOutput--;
+		node->numOutput--;
 		return resultTupleSlot;
 	}
 
@@ -88,15 +87,15 @@ ExecSetOp(SetOp *node)
 		/*
 		 * fetch a tuple from the outer subplan, unless we already did.
 		 */
-		if (setopstate->cstate.cs_OuterTupleSlot == NULL &&
-			!setopstate->subplan_done)
+		if (node->ps.ps_OuterTupleSlot == NULL &&
+			!node->subplan_done)
 		{
-			setopstate->cstate.cs_OuterTupleSlot =
-				ExecProcNode(outerPlan, (Plan *) node);
-			if (TupIsNull(setopstate->cstate.cs_OuterTupleSlot))
-				setopstate->subplan_done = true;
+			node->ps.ps_OuterTupleSlot =
+				ExecProcNode(outerPlan);
+			if (TupIsNull(node->ps.ps_OuterTupleSlot))
+				node->subplan_done = true;
 		}
-		inputTupleSlot = setopstate->cstate.cs_OuterTupleSlot;
+		inputTupleSlot = node->ps.ps_OuterTupleSlot;
 
 		if (TupIsNull(resultTupleSlot))
 		{
@@ -104,18 +103,18 @@ ExecSetOp(SetOp *node)
 			 * First of group: save a copy in result slot, and reset
 			 * duplicate-counters for new group.
 			 */
-			if (setopstate->subplan_done)
+			if (node->subplan_done)
 				return NULL;	/* no more tuples */
 			ExecStoreTuple(heap_copytuple(inputTupleSlot->val),
 						   resultTupleSlot,
 						   InvalidBuffer,
 						   true);		/* free copied tuple at
 										 * ExecClearTuple */
-			setopstate->numLeft = 0;
-			setopstate->numRight = 0;
+			node->numLeft = 0;
+			node->numRight = 0;
 			endOfGroup = false;
 		}
-		else if (setopstate->subplan_done)
+		else if (node->subplan_done)
 		{
 			/*
 			 * Reached end of input, so finish processing final group
@@ -131,9 +130,9 @@ ExecSetOp(SetOp *node)
 			if (execTuplesMatch(inputTupleSlot->val,
 								resultTupleSlot->val,
 								tupDesc,
-								node->numCols, node->dupColIdx,
-								setopstate->eqfunctions,
-								setopstate->tempContext))
+								plannode->numCols, plannode->dupColIdx,
+								node->eqfunctions,
+								node->tempContext))
 				endOfGroup = false;
 			else
 				endOfGroup = true;
@@ -146,37 +145,37 @@ ExecSetOp(SetOp *node)
 			 * Decide how many copies (if any) to emit.  This logic is
 			 * straight from the SQL92 specification.
 			 */
-			switch (node->cmd)
+			switch (plannode->cmd)
 			{
 				case SETOPCMD_INTERSECT:
-					if (setopstate->numLeft > 0 && setopstate->numRight > 0)
-						setopstate->numOutput = 1;
+					if (node->numLeft > 0 && node->numRight > 0)
+						node->numOutput = 1;
 					else
-						setopstate->numOutput = 0;
+						node->numOutput = 0;
 					break;
 				case SETOPCMD_INTERSECT_ALL:
-					setopstate->numOutput =
-						(setopstate->numLeft < setopstate->numRight) ?
-						setopstate->numLeft : setopstate->numRight;
+					node->numOutput =
+						(node->numLeft < node->numRight) ?
+						node->numLeft : node->numRight;
 					break;
 				case SETOPCMD_EXCEPT:
-					if (setopstate->numLeft > 0 && setopstate->numRight == 0)
-						setopstate->numOutput = 1;
+					if (node->numLeft > 0 && node->numRight == 0)
+						node->numOutput = 1;
 					else
-						setopstate->numOutput = 0;
+						node->numOutput = 0;
 					break;
 				case SETOPCMD_EXCEPT_ALL:
-					setopstate->numOutput =
-						(setopstate->numLeft < setopstate->numRight) ?
-						0 : (setopstate->numLeft - setopstate->numRight);
+					node->numOutput =
+						(node->numLeft < node->numRight) ?
+						0 : (node->numLeft - node->numRight);
 					break;
 				default:
 					elog(ERROR, "ExecSetOp: bogus command code %d",
-						 (int) node->cmd);
+						 (int) plannode->cmd);
 					break;
 			}
 			/* Fall out of for-loop if we have tuples to emit */
-			if (setopstate->numOutput > 0)
+			if (node->numOutput > 0)
 				break;
 			/* Else flag that we have no current tuple, and loop around */
 			ExecClearTuple(resultTupleSlot);
@@ -191,16 +190,16 @@ ExecSetOp(SetOp *node)
 			bool		isNull;
 
 			flag = DatumGetInt32(heap_getattr(inputTupleSlot->val,
-											  node->flagColIdx,
+											  plannode->flagColIdx,
 											  tupDesc,
 											  &isNull));
 			Assert(!isNull);
 			if (flag)
-				setopstate->numRight++;
+				node->numRight++;
 			else
-				setopstate->numLeft++;
+				node->numLeft++;
 			/* Set flag to fetch a new input tuple, and loop around */
-			setopstate->cstate.cs_OuterTupleSlot = NULL;
+			node->ps.ps_OuterTupleSlot = NULL;
 		}
 	}
 
@@ -208,8 +207,8 @@ ExecSetOp(SetOp *node)
 	 * If we fall out of loop, then we need to emit at least one copy of
 	 * resultTuple.
 	 */
-	Assert(setopstate->numOutput > 0);
-	setopstate->numOutput--;
+	Assert(node->numOutput > 0);
+	node->numOutput--;
 	return resultTupleSlot;
 }
 
@@ -220,23 +219,19 @@ ExecSetOp(SetOp *node)
  *		the node's subplan.
  * ----------------------------------------------------------------
  */
-bool							/* return: initialization status */
-ExecInitSetOp(SetOp *node, EState *estate, Plan *parent)
+SetOpState *
+ExecInitSetOp(SetOp *node, EState *estate)
 {
 	SetOpState *setopstate;
-	Plan	   *outerPlan;
-
-	/*
-	 * assign execution state to node
-	 */
-	node->plan.state = estate;
 
 	/*
-	 * create new SetOpState for node
+	 * create state structure
 	 */
 	setopstate = makeNode(SetOpState);
-	node->setopstate = setopstate;
-	setopstate->cstate.cs_OuterTupleSlot = NULL;
+	setopstate->ps.plan = (Plan *) node;
+	setopstate->ps.state = estate;
+
+	setopstate->ps.ps_OuterTupleSlot = NULL;
 	setopstate->subplan_done = false;
 	setopstate->numOutput = 0;
 
@@ -259,30 +254,29 @@ ExecInitSetOp(SetOp *node, EState *estate, Plan *parent)
 	/*
 	 * Tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &setopstate->cstate);
+	ExecInitResultTupleSlot(estate, &setopstate->ps);
 
 	/*
 	 * then initialize outer plan
 	 */
-	outerPlan = outerPlan((Plan *) node);
-	ExecInitNode(outerPlan, estate, (Plan *) node);
+	outerPlanState(setopstate) = ExecInitNode(outerPlan(node), estate);
 
 	/*
 	 * setop nodes do no projections, so initialize projection info for
 	 * this node appropriately
 	 */
-	ExecAssignResultTypeFromOuterPlan((Plan *) node, &setopstate->cstate);
-	setopstate->cstate.cs_ProjInfo = NULL;
+	ExecAssignResultTypeFromOuterPlan(&setopstate->ps);
+	setopstate->ps.ps_ProjInfo = NULL;
 
 	/*
 	 * Precompute fmgr lookup data for inner loop
 	 */
 	setopstate->eqfunctions =
-		execTuplesMatchPrepare(ExecGetResultType(&setopstate->cstate),
+		execTuplesMatchPrepare(ExecGetResultType(&setopstate->ps),
 							   node->numCols,
 							   node->dupColIdx);
 
-	return TRUE;
+	return setopstate;
 }
 
 int
@@ -301,34 +295,30 @@ ExecCountSlotsSetOp(SetOp *node)
  * ----------------------------------------------------------------
  */
 void
-ExecEndSetOp(SetOp *node)
+ExecEndSetOp(SetOpState *node)
 {
-	SetOpState *setopstate = node->setopstate;
-
-	ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
+	/* clean up tuple table */
+	ExecClearTuple(node->ps.ps_ResultTupleSlot);
+	node->ps.ps_OuterTupleSlot = NULL;
 
-	MemoryContextDelete(setopstate->tempContext);
+	ExecEndNode(outerPlanState(node));
 
-	/* clean up tuple table */
-	ExecClearTuple(setopstate->cstate.cs_ResultTupleSlot);
-	setopstate->cstate.cs_OuterTupleSlot = NULL;
+	MemoryContextDelete(node->tempContext);
 }
 
 
 void
-ExecReScanSetOp(SetOp *node, ExprContext *exprCtxt, Plan *parent)
+ExecReScanSetOp(SetOpState *node, ExprContext *exprCtxt)
 {
-	SetOpState *setopstate = node->setopstate;
-
-	ExecClearTuple(setopstate->cstate.cs_ResultTupleSlot);
-	setopstate->cstate.cs_OuterTupleSlot = NULL;
-	setopstate->subplan_done = false;
-	setopstate->numOutput = 0;
+	ExecClearTuple(node->ps.ps_ResultTupleSlot);
+	node->ps.ps_OuterTupleSlot = NULL;
+	node->subplan_done = false;
+	node->numOutput = 0;
 
 	/*
 	 * if chgParam of subnode is not null then plan will be re-scanned by
 	 * first ExecProcNode.
 	 */
-	if (((Plan *) node)->lefttree->chgParam == NULL)
-		ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
+	if (((PlanState *) node)->lefttree->chgParam == NULL)
+		ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
 }
diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c
index 05f36427beb..e1b4db7a51d 100644
--- a/src/backend/executor/nodeSort.c
+++ b/src/backend/executor/nodeSort.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.40 2002/11/13 00:39:47 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.41 2002/12/05 15:50:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -88,10 +88,9 @@ ExtractSortKeys(Sort *sortnode,
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
-ExecSort(Sort *node)
+ExecSort(SortState *node)
 {
 	EState	   *estate;
-	SortState  *sortstate;
 	ScanDirection dir;
 	Tuplesortstate *tuplesortstate;
 	HeapTuple	heapTuple;
@@ -104,10 +103,9 @@ ExecSort(Sort *node)
 	SO1_printf("ExecSort: %s\n",
 			   "entering routine");
 
-	sortstate = node->sortstate;
-	estate = node->plan.state;
+	estate = node->ss.ps.state;
 	dir = estate->es_direction;
-	tuplesortstate = (Tuplesortstate *) sortstate->tuplesortstate;
+	tuplesortstate = (Tuplesortstate *) node->tuplesortstate;
 
 	/*
 	 * If first time through, read all tuples from outer plan and pass
@@ -115,9 +113,10 @@ ExecSort(Sort *node)
 	 * tuplesort.
 	 */
 
-	if (!sortstate->sort_Done)
+	if (!node->sort_Done)
 	{
-		Plan	   *outerNode;
+		Sort	   *plannode = (Sort *) node->ss.ps.plan;
+		PlanState  *outerNode;
 		TupleDesc	tupDesc;
 		Oid		   *sortOperators;
 		AttrNumber *attNums;
@@ -127,8 +126,7 @@ ExecSort(Sort *node)
 
 		/*
 		 * Want to scan subplan in the forward direction while creating
-		 * the sorted data.  (Does setting my direction actually affect
-		 * the subplan?  I bet this is useless code...)
+		 * the sorted data.
 		 */
 		estate->es_direction = ForwardScanDirection;
 
@@ -138,15 +136,15 @@ ExecSort(Sort *node)
 		SO1_printf("ExecSort: %s\n",
 				   "calling tuplesort_begin");
 
-		outerNode = outerPlan((Plan *) node);
+		outerNode = outerPlanState(node);
 		tupDesc = ExecGetTupType(outerNode);
 
-		ExtractSortKeys(node, &sortOperators, &attNums);
+		ExtractSortKeys(plannode, &sortOperators, &attNums);
 
-		tuplesortstate = tuplesort_begin_heap(tupDesc, node->keycount,
+		tuplesortstate = tuplesort_begin_heap(tupDesc, plannode->keycount,
 											  sortOperators, attNums,
 											  true /* randomAccess */ );
-		sortstate->tuplesortstate = (void *) tuplesortstate;
+		node->tuplesortstate = (void *) tuplesortstate;
 
 		pfree(sortOperators);
 		pfree(attNums);
@@ -157,7 +155,7 @@ ExecSort(Sort *node)
 
 		for (;;)
 		{
-			slot = ExecProcNode(outerNode, (Plan *) node);
+			slot = ExecProcNode(outerNode);
 
 			if (TupIsNull(slot))
 				break;
@@ -178,12 +176,12 @@ ExecSort(Sort *node)
 		/*
 		 * make sure the tuple descriptor is up to date (is this needed?)
 		 */
-		ExecAssignResultType(&sortstate->csstate.cstate, tupDesc, false);
+		ExecAssignResultType(&node->ss.ps, tupDesc, false);
 
 		/*
 		 * finally set the sorted flag to true
 		 */
-		sortstate->sort_Done = true;
+		node->sort_Done = true;
 		SO1_printf("ExecSort: %s\n", "sorting done");
 	}
 
@@ -198,7 +196,7 @@ ExecSort(Sort *node)
 									   ScanDirectionIsForward(dir),
 									   &should_free);
 
-	slot = sortstate->csstate.cstate.cs_ResultTupleSlot;
+	slot = node->ss.ps.ps_ResultTupleSlot;
 	return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
 }
 
@@ -209,29 +207,24 @@ ExecSort(Sort *node)
  *		produced by the planner and initailizes its outer subtree.
  * ----------------------------------------------------------------
  */
-bool
-ExecInitSort(Sort *node, EState *estate, Plan *parent)
+SortState *
+ExecInitSort(Sort *node, EState *estate)
 {
 	SortState  *sortstate;
-	Plan	   *outerPlan;
 
 	SO1_printf("ExecInitSort: %s\n",
 			   "initializing sort node");
 
-	/*
-	 * assign the node's execution state
-	 */
-	node->plan.state = estate;
-
 	/*
 	 * create state structure
 	 */
 	sortstate = makeNode(SortState);
+	sortstate->ss.ps.plan = (Plan *) node;
+	sortstate->ss.ps.state = estate;
+
 	sortstate->sort_Done = false;
 	sortstate->tuplesortstate = NULL;
 
-	node->sortstate = sortstate;
-
 	/*
 	 * Miscellaneous initialization
 	 *
@@ -246,27 +239,26 @@ ExecInitSort(Sort *node, EState *estate, Plan *parent)
 	 *
 	 * sort nodes only return scan tuples from their sorted relation.
 	 */
-	ExecInitResultTupleSlot(estate, &sortstate->csstate.cstate);
-	ExecInitScanTupleSlot(estate, &sortstate->csstate);
+	ExecInitResultTupleSlot(estate, &sortstate->ss.ps);
+	ExecInitScanTupleSlot(estate, &sortstate->ss);
 
 	/*
 	 * initializes child nodes
 	 */
-	outerPlan = outerPlan((Plan *) node);
-	ExecInitNode(outerPlan, estate, (Plan *) node);
+	outerPlanState(sortstate) = ExecInitNode(outerPlan(node), estate);
 
 	/*
 	 * initialize tuple type.  no need to initialize projection info
 	 * because this node doesn't do projections.
 	 */
-	ExecAssignResultTypeFromOuterPlan((Plan *) node, &sortstate->csstate.cstate);
-	ExecAssignScanTypeFromOuterPlan((Plan *) node, &sortstate->csstate);
-	sortstate->csstate.cstate.cs_ProjInfo = NULL;
+	ExecAssignResultTypeFromOuterPlan(&sortstate->ss.ps);
+	ExecAssignScanTypeFromOuterPlan(&sortstate->ss);
+	sortstate->ss.ps.ps_ProjInfo = NULL;
 
 	SO1_printf("ExecInitSort: %s\n",
 			   "sort node initialized");
 
-	return TRUE;
+	return sortstate;
 }
 
 int
@@ -282,39 +274,27 @@ ExecCountSlotsSort(Sort *node)
  * ----------------------------------------------------------------
  */
 void
-ExecEndSort(Sort *node)
+ExecEndSort(SortState *node)
 {
-	SortState  *sortstate;
-	Plan	   *outerPlan;
-
-	/*
-	 * get info from the sort state
-	 */
 	SO1_printf("ExecEndSort: %s\n",
 			   "shutting down sort node");
 
-	sortstate = node->sortstate;
-
 	/*
-	 * shut down the subplan
+	 * clean out the tuple table
 	 */
-	outerPlan = outerPlan((Plan *) node);
-	ExecEndNode(outerPlan, (Plan *) node);
+	ExecClearTuple(node->ss.ss_ScanTupleSlot);
 
 	/*
-	 * clean out the tuple table
+	 * shut down the subplan
 	 */
-	ExecClearTuple(sortstate->csstate.css_ScanTupleSlot);
+	ExecEndNode(outerPlanState(node));
 
 	/*
 	 * Release tuplesort resources
 	 */
-	if (sortstate->tuplesortstate != NULL)
-		tuplesort_end((Tuplesortstate *) sortstate->tuplesortstate);
-	sortstate->tuplesortstate = NULL;
-
-	pfree(sortstate);
-	node->sortstate = NULL;
+	if (node->tuplesortstate != NULL)
+		tuplesort_end((Tuplesortstate *) node->tuplesortstate);
+	node->tuplesortstate = NULL;
 
 	SO1_printf("ExecEndSort: %s\n",
 			   "sort node shutdown");
@@ -327,17 +307,15 @@ ExecEndSort(Sort *node)
  * ----------------------------------------------------------------
  */
 void
-ExecSortMarkPos(Sort *node)
+ExecSortMarkPos(SortState *node)
 {
-	SortState  *sortstate = node->sortstate;
-
 	/*
 	 * if we haven't sorted yet, just return
 	 */
-	if (!sortstate->sort_Done)
+	if (!node->sort_Done)
 		return;
 
-	tuplesort_markpos((Tuplesortstate *) sortstate->tuplesortstate);
+	tuplesort_markpos((Tuplesortstate *) node->tuplesortstate);
 }
 
 /* ----------------------------------------------------------------
@@ -347,36 +325,32 @@ ExecSortMarkPos(Sort *node)
  * ----------------------------------------------------------------
  */
 void
-ExecSortRestrPos(Sort *node)
+ExecSortRestrPos(SortState *node)
 {
-	SortState  *sortstate = node->sortstate;
-
 	/*
 	 * if we haven't sorted yet, just return.
 	 */
-	if (!sortstate->sort_Done)
+	if (!node->sort_Done)
 		return;
 
 	/*
 	 * restore the scan to the previously marked position
 	 */
-	tuplesort_restorepos((Tuplesortstate *) sortstate->tuplesortstate);
+	tuplesort_restorepos((Tuplesortstate *) node->tuplesortstate);
 }
 
 void
-ExecReScanSort(Sort *node, ExprContext *exprCtxt, Plan *parent)
+ExecReScanSort(SortState *node, ExprContext *exprCtxt)
 {
-	SortState  *sortstate = node->sortstate;
-
 	/*
 	 * If we haven't sorted yet, just return. If outerplan' chgParam is
 	 * not NULL then it will be re-scanned by ExecProcNode, else - no
 	 * reason to re-scan it at all.
 	 */
-	if (!sortstate->sort_Done)
+	if (!node->sort_Done)
 		return;
 
-	ExecClearTuple(sortstate->csstate.cstate.cs_ResultTupleSlot);
+	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
 
 	/*
 	 * If subnode is to be rescanned then we forget previous sort results;
@@ -384,12 +358,12 @@ ExecReScanSort(Sort *node, ExprContext *exprCtxt, Plan *parent)
 	 *
 	 * Otherwise we can just rewind and rescan the sorted output.
 	 */
-	if (((Plan *) node)->lefttree->chgParam != NULL)
+	if (((PlanState *) node)->lefttree->chgParam != NULL)
 	{
-		sortstate->sort_Done = false;
-		tuplesort_end((Tuplesortstate *) sortstate->tuplesortstate);
-		sortstate->tuplesortstate = NULL;
+		node->sort_Done = false;
+		tuplesort_end((Tuplesortstate *) node->tuplesortstate);
+		node->tuplesortstate = NULL;
 	}
 	else
-		tuplesort_rescan((Tuplesortstate *) sortstate->tuplesortstate);
+		tuplesort_rescan((Tuplesortstate *) node->tuplesortstate);
 }
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 73ff6370fbc..195634c1289 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.34 2002/11/26 03:01:57 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.35 2002/12/05 15:50:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -27,14 +27,15 @@
 
 /* ----------------------------------------------------------------
  *		ExecSubPlan(node)
- *
  * ----------------------------------------------------------------
  */
 Datum
-ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
+ExecSubPlan(SubPlanState *node, List *pvar,
+			ExprContext *econtext, bool *isNull)
 {
-	Plan	   *plan = node->plan;
-	SubLink    *sublink = node->sublink;
+	PlanState  *planstate = node->planstate;
+	SubPlan	   *subplan = (SubPlan *) node->ps.plan;
+	SubLink    *sublink = subplan->sublink;
 	SubLinkType subLinkType = sublink->subLinkType;
 	bool		useor = sublink->useor;
 	MemoryContext oldcontext;
@@ -49,15 +50,15 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
 	 */
 	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
 
-	if (node->setParam != NIL)
+	if (subplan->setParam != NIL)
 		elog(ERROR, "ExecSubPlan: can't set parent params from subquery");
 
 	/*
 	 * Set Params of this plan from parent plan correlation Vars
 	 */
-	if (node->parParam != NIL)
+	if (subplan->parParam != NIL)
 	{
-		foreach(lst, node->parParam)
+		foreach(lst, subplan->parParam)
 		{
 			ParamExecData *prm;
 
@@ -69,11 +70,12 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
 												   NULL);
 			pvar = lnext(pvar);
 		}
-		plan->chgParam = nconc(plan->chgParam, listCopy(node->parParam));
+		planstate->chgParam = nconc(planstate->chgParam,
+									listCopy(subplan->parParam));
 	}
 	Assert(pvar == NIL);
 
-	ExecReScan(plan, NULL, NULL);
+	ExecReScan(planstate, NULL);
 
 	/*
 	 * For all sublink types except EXPR_SUBLINK, the result is boolean as
@@ -96,9 +98,9 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
 	result = BoolGetDatum(subLinkType == ALL_SUBLINK);
 	*isNull = false;
 
-	for (slot = ExecProcNode(plan, NULL);
+	for (slot = ExecProcNode(planstate);
 		 !TupIsNull(slot);
-		 slot = ExecProcNode(plan, NULL))
+		 slot = ExecProcNode(planstate))
 	{
 		HeapTuple	tup = slot->val;
 		TupleDesc	tdesc = slot->ttc_tupleDescriptor;
@@ -283,13 +285,37 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
 
 /* ----------------------------------------------------------------
  *		ExecInitSubPlan
- *
  * ----------------------------------------------------------------
  */
-bool
-ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent)
+SubPlanState *
+ExecInitSubPlan(SubPlan *node, EState *estate)
 {
-	EState	   *sp_estate = CreateExecutorState();
+	SubPlanState *subplanstate;
+	EState	   *sp_estate;
+
+	/*
+	 * Do access checking on the rangetable entries in the subquery.
+	 * Here, we assume the subquery is a SELECT.
+	 */
+	ExecCheckRTPerms(node->rtable, CMD_SELECT);
+
+	/*
+	 * create state structure
+	 */
+	subplanstate = makeNode(SubPlanState);
+	subplanstate->ps.plan = (Plan *) node;
+	subplanstate->ps.state = estate;
+
+	subplanstate->needShutdown = false;
+	subplanstate->curTuple = NULL;
+
+	/* XXX temporary hack */
+	node->pstate = subplanstate;
+
+	/*
+	 * create an EState for the subplan
+	 */
+	sp_estate = CreateExecutorState();
 
 	sp_estate->es_range_table = node->rtable;
 	sp_estate->es_param_list_info = estate->es_param_list_info;
@@ -297,14 +323,14 @@ ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent)
 	sp_estate->es_tupleTable =
 		ExecCreateTupleTable(ExecCountSlotsNode(node->plan) + 10);
 	sp_estate->es_snapshot = estate->es_snapshot;
+	sp_estate->es_instrument = estate->es_instrument;
 
-	node->needShutdown = false;
-	node->curTuple = NULL;
-
-	if (!ExecInitNode(node->plan, sp_estate, parent))
-		return false;
+	/*
+	 * Start up the subplan
+	 */
+	subplanstate->planstate = ExecInitNode(node->plan, sp_estate);
 
-	node->needShutdown = true;	/* now we need to shutdown the subplan */
+	subplanstate->needShutdown = true;	/* now we need to shutdown the subplan */
 
 	/*
 	 * If this plan is un-correlated or undirect correlated one and want
@@ -318,7 +344,7 @@ ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent)
 		{
 			ParamExecData *prm = &(estate->es_param_exec_vals[lfirsti(lst)]);
 
-			prm->execPlan = node;
+			prm->execPlan = subplanstate;
 		}
 
 		/*
@@ -328,7 +354,7 @@ ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent)
 		 */
 	}
 
-	return true;
+	return subplanstate;
 }
 
 /* ----------------------------------------------------------------
@@ -345,10 +371,12 @@ ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent)
  * ----------------------------------------------------------------
  */
 void
-ExecSetParamPlan(SubPlan *node, ExprContext *econtext)
+ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 {
-	Plan	   *plan = node->plan;
-	SubLink    *sublink = node->sublink;
+	PlanState  *planstate = node->planstate;
+	SubPlan	   *subplan = (SubPlan *) node->ps.plan;
+	SubLink    *sublink = subplan->sublink;
+	EState	   *estate = node->ps.state;
 	MemoryContext oldcontext;
 	TupleTableSlot *slot;
 	List	   *lst;
@@ -364,12 +392,12 @@ ExecSetParamPlan(SubPlan *node, ExprContext *econtext)
 		sublink->subLinkType == ALL_SUBLINK)
 		elog(ERROR, "ExecSetParamPlan: ANY/ALL subselect unsupported");
 
-	if (plan->chgParam != NULL)
-		ExecReScan(plan, NULL, NULL);
+	if (planstate->chgParam != NULL)
+		ExecReScan(planstate, NULL);
 
-	for (slot = ExecProcNode(plan, NULL);
+	for (slot = ExecProcNode(planstate);
 		 !TupIsNull(slot);
-		 slot = ExecProcNode(plan, NULL))
+		 slot = ExecProcNode(planstate))
 	{
 		HeapTuple	tup = slot->val;
 		TupleDesc	tdesc = slot->ttc_tupleDescriptor;
@@ -377,7 +405,7 @@ ExecSetParamPlan(SubPlan *node, ExprContext *econtext)
 
 		if (sublink->subLinkType == EXISTS_SUBLINK)
 		{
-			ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]);
+			ParamExecData *prm = &(estate->es_param_exec_vals[lfirsti(subplan->setParam)]);
 
 			prm->execPlan = NULL;
 			prm->value = BoolGetDatum(true);
@@ -404,9 +432,9 @@ ExecSetParamPlan(SubPlan *node, ExprContext *econtext)
 			heap_freetuple(node->curTuple);
 		node->curTuple = tup;
 
-		foreach(lst, node->setParam)
+		foreach(lst, subplan->setParam)
 		{
-			ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]);
+			ParamExecData *prm = &(estate->es_param_exec_vals[lfirsti(lst)]);
 
 			prm->execPlan = NULL;
 			prm->value = heap_getattr(tup, i, tdesc, &(prm->isnull));
@@ -418,7 +446,7 @@ ExecSetParamPlan(SubPlan *node, ExprContext *econtext)
 	{
 		if (sublink->subLinkType == EXISTS_SUBLINK)
 		{
-			ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]);
+			ParamExecData *prm = &(estate->es_param_exec_vals[lfirsti(subplan->setParam)]);
 
 			prm->execPlan = NULL;
 			prm->value = BoolGetDatum(false);
@@ -426,9 +454,9 @@ ExecSetParamPlan(SubPlan *node, ExprContext *econtext)
 		}
 		else
 		{
-			foreach(lst, node->setParam)
+			foreach(lst, subplan->setParam)
 			{
-				ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]);
+				ParamExecData *prm = &(estate->es_param_exec_vals[lfirsti(lst)]);
 
 				prm->execPlan = NULL;
 				prm->value = (Datum) 0;
@@ -437,9 +465,9 @@ ExecSetParamPlan(SubPlan *node, ExprContext *econtext)
 		}
 	}
 
-	if (plan->extParam == NULL) /* un-correlated ... */
+	if (planstate->plan->extParam == NULL) /* un-correlated ... */
 	{
-		ExecEndNode(plan, NULL);
+		ExecEndNode(planstate);
 		node->needShutdown = false;
 	}
 
@@ -451,11 +479,11 @@ ExecSetParamPlan(SubPlan *node, ExprContext *econtext)
  * ----------------------------------------------------------------
  */
 void
-ExecEndSubPlan(SubPlan *node)
+ExecEndSubPlan(SubPlanState *node)
 {
 	if (node->needShutdown)
 	{
-		ExecEndNode(node->plan, NULL);
+		ExecEndNode(node->planstate);
 		node->needShutdown = false;
 	}
 	if (node->curTuple)
@@ -466,33 +494,34 @@ ExecEndSubPlan(SubPlan *node)
 }
 
 void
-ExecReScanSetParamPlan(SubPlan *node, Plan *parent)
+ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent)
 {
-	Plan	   *plan = node->plan;
+	PlanState  *planstate = node->planstate;
+	SubPlan	   *subplan = (SubPlan *) node->ps.plan;
+	EState	   *estate = node->ps.state;
 	List	   *lst;
 
-	if (node->parParam != NULL)
+	if (subplan->parParam != NULL)
 		elog(ERROR, "ExecReScanSetParamPlan: direct correlated subquery unsupported, yet");
-	if (node->setParam == NULL)
+	if (subplan->setParam == NULL)
 		elog(ERROR, "ExecReScanSetParamPlan: setParam list is NULL");
-	if (plan->extParam == NULL)
+	if (planstate->plan->extParam == NULL)
 		elog(ERROR, "ExecReScanSetParamPlan: extParam list of plan is NULL");
 
 	/*
 	 * Don't actual re-scan: ExecSetParamPlan does re-scan if
-	 * node->plan->chgParam is not NULL... ExecReScan (plan, NULL, NULL);
+	 * subplan->plan->chgParam is not NULL... ExecReScan (planstate, NULL);
 	 */
 
 	/*
 	 * Mark this subplan's output parameters as needing recalculation
 	 */
-	foreach(lst, node->setParam)
+	foreach(lst, subplan->setParam)
 	{
-		ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]);
+		ParamExecData *prm = &(estate->es_param_exec_vals[lfirsti(lst)]);
 
 		prm->execPlan = node;
 	}
 
-	parent->chgParam = nconc(parent->chgParam, listCopy(node->setParam));
-
+	parent->chgParam = nconc(parent->chgParam, listCopy(subplan->setParam));
 }
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 982dd0236ca..ba0ed8635b7 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.13 2002/06/20 20:29:28 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.14 2002/12/05 15:50:33 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,7 +35,7 @@
 #include "parser/parsetree.h"
 #include "tcop/pquery.h"
 
-static TupleTableSlot *SubqueryNext(SubqueryScan *node);
+static TupleTableSlot *SubqueryNext(SubqueryScanState *node);
 
 /* ----------------------------------------------------------------
  *						Scan Support
@@ -48,9 +48,8 @@ static TupleTableSlot *SubqueryNext(SubqueryScan *node);
  * ----------------------------------------------------------------
  */
 static TupleTableSlot *
-SubqueryNext(SubqueryScan *node)
+SubqueryNext(SubqueryScanState *node)
 {
-	SubqueryScanState *subquerystate;
 	EState	   *estate;
 	ScanDirection direction;
 	TupleTableSlot *slot;
@@ -58,8 +57,7 @@ SubqueryNext(SubqueryScan *node)
 	/*
 	 * get information from the estate and scan state
 	 */
-	estate = node->scan.plan.state;
-	subquerystate = (SubqueryScanState *) node->scan.scanstate;
+	estate = node->ss.ps.state;
 	direction = estate->es_direction;
 
 	/*
@@ -70,11 +68,11 @@ SubqueryNext(SubqueryScan *node)
 	/*
 	 * get the next tuple from the sub-query
 	 */
-	subquerystate->sss_SubEState->es_direction = direction;
+	node->sss_SubEState->es_direction = direction;
 
-	slot = ExecProcNode(node->subplan, (Plan *) node);
+	slot = ExecProcNode(node->subplan);
 
-	subquerystate->csstate.css_ScanTupleSlot = slot;
+	node->ss.ss_ScanTupleSlot = slot;
 
 	return slot;
 }
@@ -90,20 +88,20 @@ SubqueryNext(SubqueryScan *node)
  */
 
 TupleTableSlot *
-ExecSubqueryScan(SubqueryScan *node)
+ExecSubqueryScan(SubqueryScanState *node)
 {
 	/*
 	 * use SubqueryNext as access method
 	 */
-	return ExecScan(&node->scan, (ExecScanAccessMtd) SubqueryNext);
+	return ExecScan(&node->ss, (ExecScanAccessMtd) SubqueryNext);
 }
 
 /* ----------------------------------------------------------------
  *		ExecInitSubqueryScan
  * ----------------------------------------------------------------
  */
-bool
-ExecInitSubqueryScan(SubqueryScan *node, EState *estate, Plan *parent)
+SubqueryScanState *
+ExecInitSubqueryScan(SubqueryScan *node, EState *estate)
 {
 	SubqueryScanState *subquerystate;
 	RangeTblEntry *rte;
@@ -112,33 +110,39 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, Plan *parent)
 	/*
 	 * SubqueryScan should not have any "normal" children.
 	 */
-	Assert(outerPlan((Plan *) node) == NULL);
-	Assert(innerPlan((Plan *) node) == NULL);
+	Assert(outerPlan(node) == NULL);
+	Assert(innerPlan(node) == NULL);
 
 	/*
-	 * assign the node's execution state
-	 */
-	node->scan.plan.state = estate;
-
-	/*
-	 * create new SubqueryScanState for node
+	 * create state structure
 	 */
 	subquerystate = makeNode(SubqueryScanState);
-	node->scan.scanstate = (CommonScanState *) subquerystate;
+	subquerystate->ss.ps.plan = (Plan *) node;
+	subquerystate->ss.ps.state = estate;
 
 	/*
 	 * Miscellaneous initialization
 	 *
 	 * create expression context for node
 	 */
-	ExecAssignExprContext(estate, &subquerystate->csstate.cstate);
+	ExecAssignExprContext(estate, &subquerystate->ss.ps);
+
+	/*
+	 * initialize child expressions
+	 */
+	subquerystate->ss.ps.targetlist = (List *)
+		ExecInitExpr((Node *) node->scan.plan.targetlist,
+					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual = (List *)
+		ExecInitExpr((Node *) node->scan.plan.qual,
+					 (PlanState *) subquerystate);
 
 #define SUBQUERYSCAN_NSLOTS 1
 
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &subquerystate->csstate.cstate);
+	ExecInitResultTupleSlot(estate, &subquerystate->ss.ps);
 
 	/*
 	 * initialize subquery
@@ -157,20 +161,20 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, Plan *parent)
 	sp_estate->es_tupleTable =
 		ExecCreateTupleTable(ExecCountSlotsNode(node->subplan) + 10);
 	sp_estate->es_snapshot = estate->es_snapshot;
+	sp_estate->es_instrument = estate->es_instrument;
 
-	if (!ExecInitNode(node->subplan, sp_estate, (Plan *) node))
-		return false;
+	subquerystate->subplan = ExecInitNode(node->subplan, sp_estate);
 
-	subquerystate->csstate.css_ScanTupleSlot = NULL;
-	subquerystate->csstate.cstate.cs_TupFromTlist = false;
+	subquerystate->ss.ss_ScanTupleSlot = NULL;
+	subquerystate->ss.ps.ps_TupFromTlist = false;
 
 	/*
 	 * initialize tuple type
 	 */
-	ExecAssignResultTypeFromTL((Plan *) node, &subquerystate->csstate.cstate);
-	ExecAssignProjectionInfo((Plan *) node, &subquerystate->csstate.cstate);
+	ExecAssignResultTypeFromTL(&subquerystate->ss.ps);
+	ExecAssignProjectionInfo(&subquerystate->ss.ps);
 
-	return TRUE;
+	return subquerystate;
 }
 
 int
@@ -191,42 +195,31 @@ ExecCountSlotsSubqueryScan(SubqueryScan *node)
  * ----------------------------------------------------------------
  */
 void
-ExecEndSubqueryScan(SubqueryScan *node)
+ExecEndSubqueryScan(SubqueryScanState *node)
 {
-	SubqueryScanState *subquerystate;
-
 	/*
-	 * get information from node
+	 * Free the projection info and the scan attribute info
 	 */
-	subquerystate = (SubqueryScanState *) node->scan.scanstate;
+	ExecFreeProjectionInfo(&node->ss.ps);
+	ExecFreeExprContext(&node->ss.ps);
 
 	/*
-	 * Free the projection info and the scan attribute info
-	 *
-	 * Note: we don't ExecFreeResultType(subquerystate) because the rule
-	 * manager depends on the tupType returned by ExecMain().  So for now,
-	 * this is freed at end-transaction time.  -cim 6/2/91
+	 * clean out the upper tuple table
 	 */
-	ExecFreeProjectionInfo(&subquerystate->csstate.cstate);
-	ExecFreeExprContext(&subquerystate->csstate.cstate);
+	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
 
 	/*
 	 * close down subquery
 	 */
-	ExecEndNode(node->subplan, (Plan *) node);
+	ExecEndNode(node->subplan);
 
 	/*
 	 * clean up subquery's tuple table
 	 */
-	subquerystate->csstate.css_ScanTupleSlot = NULL;
-	ExecDropTupleTable(subquerystate->sss_SubEState->es_tupleTable, true);
+	node->ss.ss_ScanTupleSlot = NULL;
+	ExecDropTupleTable(node->sss_SubEState->es_tupleTable, true);
 
 	/* XXX we seem to be leaking the sub-EState... */
-
-	/*
-	 * clean out the upper tuple table
-	 */
-	ExecClearTuple(subquerystate->csstate.cstate.cs_ResultTupleSlot);
 }
 
 /* ----------------------------------------------------------------
@@ -236,27 +229,25 @@ ExecEndSubqueryScan(SubqueryScan *node)
  * ----------------------------------------------------------------
  */
 void
-ExecSubqueryReScan(SubqueryScan *node, ExprContext *exprCtxt, Plan *parent)
+ExecSubqueryReScan(SubqueryScanState *node, ExprContext *exprCtxt)
 {
-	SubqueryScanState *subquerystate;
 	EState	   *estate;
 
-	subquerystate = (SubqueryScanState *) node->scan.scanstate;
-	estate = node->scan.plan.state;
+	estate = node->ss.ps.state;
 
 	/*
 	 * ExecReScan doesn't know about my subplan, so I have to do
 	 * changed-parameter signaling myself.
 	 */
-	if (node->scan.plan.chgParam != NULL)
-		SetChangedParamList(node->subplan, node->scan.plan.chgParam);
+	if (node->ss.ps.chgParam != NULL)
+		SetChangedParamList(node->subplan, node->ss.ps.chgParam);
 
 	/*
 	 * if chgParam of subnode is not null then plan will be re-scanned by
 	 * first ExecProcNode.
 	 */
 	if (node->subplan->chgParam == NULL)
-		ExecReScan(node->subplan, NULL, (Plan *) node);
+		ExecReScan(node->subplan, NULL);
 
-	subquerystate->csstate.css_ScanTupleSlot = NULL;
+	node->ss.ss_ScanTupleSlot = NULL;
 }
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 0fb7c6df4a8..53b3c26d35a 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.27 2002/11/30 05:21:01 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.28 2002/12/05 15:50:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,7 +30,7 @@
 #include "parser/parsetree.h"
 
 static int	TidListCreate(List *, ExprContext *, ItemPointerData[]);
-static TupleTableSlot *TidNext(TidScan *node);
+static TupleTableSlot *TidNext(TidScanState *node);
 
 static int
 TidListCreate(List *evalList, ExprContext *econtext, ItemPointerData tidList[])
@@ -65,19 +65,17 @@ TidListCreate(List *evalList, ExprContext *econtext, ItemPointerData tidList[])
  * ----------------------------------------------------------------
  */
 static TupleTableSlot *
-TidNext(TidScan *node)
+TidNext(TidScanState *node)
 {
 	EState	   *estate;
-	CommonScanState *scanstate;
-	TidScanState *tidstate;
 	ScanDirection direction;
 	Snapshot	snapshot;
 	Relation	heapRelation;
 	HeapTuple	tuple;
 	TupleTableSlot *slot;
+	Index		scanrelid;
 	Buffer		buffer = InvalidBuffer;
 	int			numTids;
-
 	bool		bBackward;
 	int			tidNumber;
 	ItemPointerData *tidList;
@@ -85,15 +83,14 @@ TidNext(TidScan *node)
 	/*
 	 * extract necessary information from tid scan node
 	 */
-	estate = node->scan.plan.state;
+	estate = node->ss.ps.state;
 	direction = estate->es_direction;
 	snapshot = estate->es_snapshot;
-	scanstate = node->scan.scanstate;
-	tidstate = node->tidstate;
-	heapRelation = scanstate->css_currentRelation;
-	numTids = tidstate->tss_NumTids;
-	tidList = tidstate->tss_TidList;
-	slot = scanstate->css_ScanTupleSlot;
+	heapRelation = node->ss.ss_currentRelation;
+	numTids = node->tss_NumTids;
+	tidList = node->tss_TidList;
+	slot = node->ss.ss_ScanTupleSlot;
+	scanrelid = ((TidScan *) node->ss.ps.plan)->scan.scanrelid;
 
 	/*
 	 * Check if we are evaluating PlanQual for tuple of this relation.
@@ -102,10 +99,10 @@ TidNext(TidScan *node)
 	 * switching in Init/ReScan plan...
 	 */
 	if (estate->es_evTuple != NULL &&
-		estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
+		estate->es_evTuple[scanrelid - 1] != NULL)
 	{
 		ExecClearTuple(slot);
-		if (estate->es_evTupleNull[node->scan.scanrelid - 1])
+		if (estate->es_evTupleNull[scanrelid - 1])
 			return slot;		/* return empty slot */
 
 		/*
@@ -113,15 +110,15 @@ TidNext(TidScan *node)
 		 * list? In runtime-key case this is not certain, is it?
 		 */
 
-		ExecStoreTuple(estate->es_evTuple[node->scan.scanrelid - 1],
+		ExecStoreTuple(estate->es_evTuple[scanrelid - 1],
 					   slot, InvalidBuffer, false);
 
 		/* Flag for the next call that no more tuples */
-		estate->es_evTupleNull[node->scan.scanrelid - 1] = true;
+		estate->es_evTupleNull[scanrelid - 1] = true;
 		return (slot);
 	}
 
-	tuple = &(tidstate->tss_htup);
+	tuple = &(node->tss_htup);
 
 	/*
 	 * ok, now that we have what we need, fetch an tid tuple. if scanning
@@ -131,26 +128,26 @@ TidNext(TidScan *node)
 	bBackward = ScanDirectionIsBackward(direction);
 	if (bBackward)
 	{
-		tidNumber = numTids - tidstate->tss_TidPtr - 1;
+		tidNumber = numTids - node->tss_TidPtr - 1;
 		if (tidNumber < 0)
 		{
 			tidNumber = 0;
-			tidstate->tss_TidPtr = numTids - 1;
+			node->tss_TidPtr = numTids - 1;
 		}
 	}
 	else
 	{
-		if ((tidNumber = tidstate->tss_TidPtr) < 0)
+		if ((tidNumber = node->tss_TidPtr) < 0)
 		{
 			tidNumber = 0;
-			tidstate->tss_TidPtr = 0;
+			node->tss_TidPtr = 0;
 		}
 	}
 	while (tidNumber < numTids)
 	{
 		bool		slot_is_valid = false;
 
-		tuple->t_self = tidList[tidstate->tss_TidPtr];
+		tuple->t_self = tidList[node->tss_TidPtr];
 		if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
 		{
 			bool		prev_matches = false;
@@ -181,7 +178,7 @@ TidNext(TidScan *node)
 			 * do this by passing the tuple through ExecQual and look for
 			 * failure with all previous qualifications.
 			 */
-			for (prev_tid = 0; prev_tid < tidstate->tss_TidPtr;
+			for (prev_tid = 0; prev_tid < node->tss_TidPtr;
 				 prev_tid++)
 			{
 				if (ItemPointerEquals(&tidList[prev_tid], &tuple->t_self))
@@ -197,9 +194,9 @@ TidNext(TidScan *node)
 		}
 		tidNumber++;
 		if (bBackward)
-			tidstate->tss_TidPtr--;
+			node->tss_TidPtr--;
 		else
-			tidstate->tss_TidPtr++;
+			node->tss_TidPtr++;
 		if (slot_is_valid)
 			return slot;
 	}
@@ -231,12 +228,12 @@ TidNext(TidScan *node)
  * ----------------------------------------------------------------
  */
 TupleTableSlot *
-ExecTidScan(TidScan *node)
+ExecTidScan(TidScanState *node)
 {
 	/*
 	 * use TidNext as access method
 	 */
-	return ExecScan(&node->scan, (ExecScanAccessMtd) TidNext);
+	return ExecScan(&node->ss, (ExecScanAccessMtd) TidNext);
 }
 
 /* ----------------------------------------------------------------
@@ -244,42 +241,30 @@ ExecTidScan(TidScan *node)
  * ----------------------------------------------------------------
  */
 void
-ExecTidReScan(TidScan *node, ExprContext *exprCtxt, Plan *parent)
+ExecTidReScan(TidScanState *node, ExprContext *exprCtxt)
 {
 	EState	   *estate;
-	TidScanState *tidstate;
 	ItemPointerData *tidList;
+	Index		scanrelid;
 
-	estate = node->scan.plan.state;
-	tidstate = node->tidstate;
-	tidList = tidstate->tss_TidList;
+	estate = node->ss.ps.state;
+	tidList = node->tss_TidList;
+	scanrelid = ((TidScan *) node->ss.ps.plan)->scan.scanrelid;
 
 	/* If we are being passed an outer tuple, save it for runtime key calc */
 	if (exprCtxt != NULL)
-		node->scan.scanstate->cstate.cs_ExprContext->ecxt_outertuple =
+		node->ss.ps.ps_ExprContext->ecxt_outertuple =
 			exprCtxt->ecxt_outertuple;
 
-	/* do runtime calc of target TIDs, if needed */
-	if (node->needRescan)
-		tidstate->tss_NumTids =
-			TidListCreate(node->tideval,
-						  node->scan.scanstate->cstate.cs_ExprContext,
-						  tidList);
-
 	/* If this is re-scanning of PlanQual ... */
 	if (estate->es_evTuple != NULL &&
-		estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
+		estate->es_evTuple[scanrelid - 1] != NULL)
 	{
-		estate->es_evTupleNull[node->scan.scanrelid - 1] = false;
+		estate->es_evTupleNull[scanrelid - 1] = false;
 		return;
 	}
 
-	tidstate->tss_TidPtr = -1;
-
-	/*
-	 * perhaps return something meaningful
-	 */
-	return;
+	node->tss_TidPtr = -1;
 }
 
 /* ----------------------------------------------------------------
@@ -290,18 +275,13 @@ ExecTidReScan(TidScan *node, ExprContext *exprCtxt, Plan *parent)
  * ----------------------------------------------------------------
  */
 void
-ExecEndTidScan(TidScan *node)
+ExecEndTidScan(TidScanState *node)
 {
-	CommonScanState *scanstate;
-	TidScanState *tidstate;
-
 	/*
 	 * extract information from the node
 	 */
-	scanstate = node->scan.scanstate;
-	tidstate = node->tidstate;
-	if (tidstate && tidstate->tss_TidList)
-		pfree(tidstate->tss_TidList);
+	if (node && node->tss_TidList)
+		pfree(node->tss_TidList);
 
 	/*
 	 * Free the projection info and the scan attribute info
@@ -310,8 +290,14 @@ ExecEndTidScan(TidScan *node)
 	 * depends on the tupType returned by ExecMain().  So for now, this is
 	 * freed at end-transaction time.  -cim 6/2/91
 	 */
-	ExecFreeProjectionInfo(&scanstate->cstate);
-	ExecFreeExprContext(&scanstate->cstate);
+	ExecFreeProjectionInfo(&node->ss.ps);
+	ExecFreeExprContext(&node->ss.ps);
+
+	/*
+	 * clear out tuple table slots
+	 */
+	ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
+	ExecClearTuple(node->ss.ss_ScanTupleSlot);
 
 	/*
 	 * close the heap relation.
@@ -320,13 +306,7 @@ ExecEndTidScan(TidScan *node)
 	 * ExecInitTidScan.  This lock should be held till end of transaction.
 	 * (There is a faction that considers this too much locking, however.)
 	 */
-	heap_close(scanstate->css_currentRelation, NoLock);
-
-	/*
-	 * clear out tuple table slots
-	 */
-	ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
-	ExecClearTuple(scanstate->css_ScanTupleSlot);
+	heap_close(node->ss.ss_currentRelation, NoLock);
 }
 
 /* ----------------------------------------------------------------
@@ -337,12 +317,9 @@ ExecEndTidScan(TidScan *node)
  * ----------------------------------------------------------------
  */
 void
-ExecTidMarkPos(TidScan *node)
+ExecTidMarkPos(TidScanState *node)
 {
-	TidScanState *tidstate;
-
-	tidstate = node->tidstate;
-	tidstate->tss_MarkTidPtr = tidstate->tss_TidPtr;
+	node->tss_MarkTidPtr = node->tss_TidPtr;
 }
 
 /* ----------------------------------------------------------------
@@ -355,12 +332,9 @@ ExecTidMarkPos(TidScan *node)
  * ----------------------------------------------------------------
  */
 void
-ExecTidRestrPos(TidScan *node)
+ExecTidRestrPos(TidScanState *node)
 {
-	TidScanState *tidstate;
-
-	tidstate = node->tidstate;
-	tidstate->tss_TidPtr = tidstate->tss_MarkTidPtr;
+	node->tss_TidPtr = node->tss_MarkTidPtr;
 }
 
 /* ----------------------------------------------------------------
@@ -374,11 +348,10 @@ ExecTidRestrPos(TidScan *node)
  *		  estate: the execution state initialized in InitPlan.
  * ----------------------------------------------------------------
  */
-bool
-ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
+TidScanState *
+ExecInitTidScan(TidScan *node, EState *estate)
 {
 	TidScanState *tidstate;
-	CommonScanState *scanstate;
 	ItemPointerData *tidList;
 	int			numTids;
 	int			tidPtr;
@@ -390,56 +363,50 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
 	List	   *execParam = NIL;
 
 	/*
-	 * assign execution state to node
+	 * create state structure
 	 */
-	node->scan.plan.state = estate;
+	tidstate = makeNode(TidScanState);
+	tidstate->ss.ps.plan = (Plan *) node;
+	tidstate->ss.ps.state = estate;
 
 	/*
-	 * Part 1)	initialize scan state
+	 * Miscellaneous initialization
 	 *
-	 * create new CommonScanState for node
+	 * create expression context for node
 	 */
-	scanstate = makeNode(CommonScanState);
-	node->scan.scanstate = scanstate;
+	ExecAssignExprContext(estate, &tidstate->ss.ps);
 
 	/*
-	 * Miscellaneous initialization
-	 *
-	 * create expression context for node
+	 * initialize child expressions
 	 */
-	ExecAssignExprContext(estate, &scanstate->cstate);
+	tidstate->ss.ps.targetlist = (List *)
+		ExecInitExpr((Node *) node->scan.plan.targetlist,
+					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual = (List *)
+		ExecInitExpr((Node *) node->scan.plan.qual,
+					 (PlanState *) tidstate);
 
 #define TIDSCAN_NSLOTS 2
 
 	/*
 	 * tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &scanstate->cstate);
-	ExecInitScanTupleSlot(estate, scanstate);
+	ExecInitResultTupleSlot(estate, &tidstate->ss.ps);
+	ExecInitScanTupleSlot(estate, &tidstate->ss);
 
 	/*
 	 * initialize projection info.	result type comes from scan desc
 	 * below..
 	 */
-	ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate);
-
-	/*
-	 * Part 2)	initialize tid scan state
-	 *
-	 * create new TidScanState for node
-	 */
-	tidstate = makeNode(TidScanState);
-	node->tidstate = tidstate;
+	ExecAssignProjectionInfo(&tidstate->ss.ps);
 
 	/*
 	 * get the tid node information
 	 */
 	tidList = (ItemPointerData *) palloc(length(node->tideval) * sizeof(ItemPointerData));
-	numTids = 0;
-	if (!node->needRescan)
-		numTids = TidListCreate(node->tideval,
-								scanstate->cstate.cs_ExprContext,
-								tidList);
+	numTids = TidListCreate(node->tideval,
+							tidstate->ss.ps.ps_ExprContext,
+							tidList);
 	tidPtr = -1;
 
 	CXT1_printf("ExecInitTidScan: context is %d\n", CurrentMemoryContext);
@@ -465,25 +432,25 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
 
 	currentRelation = heap_open(reloid, AccessShareLock);
 
-	scanstate->css_currentRelation = currentRelation;
-	scanstate->css_currentScanDesc = NULL;		/* no heap scan here */
+	tidstate->ss.ss_currentRelation = currentRelation;
+	tidstate->ss.ss_currentScanDesc = NULL;		/* no heap scan here */
 
 	/*
 	 * get the scan type from the relation descriptor.
 	 */
-	ExecAssignScanType(scanstate, RelationGetDescr(currentRelation), false);
-	ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
+	ExecAssignScanType(&tidstate->ss, RelationGetDescr(currentRelation), false);
+	ExecAssignResultTypeFromTL(&tidstate->ss.ps);
 
 	/*
 	 * if there are some PARAM_EXEC in skankeys then force tid rescan on
 	 * first scan.
 	 */
-	((Plan *) node)->chgParam = execParam;
+	tidstate->ss.ps.chgParam = execParam;
 
 	/*
 	 * all done.
 	 */
-	return TRUE;
+	return tidstate;
 }
 
 int
diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c
index b71403de0b8..88b08061f99 100644
--- a/src/backend/executor/nodeUnique.c
+++ b/src/backend/executor/nodeUnique.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.34 2002/06/20 20:29:28 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.35 2002/12/05 15:50:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,7 +21,6 @@
  * NOTES
  *		Assumes tuples returned from subplan arrive in
  *		sorted order.
- *
  */
 
 #include "postgres.h"
@@ -39,21 +38,20 @@
  * ----------------------------------------------------------------
  */
 TupleTableSlot *				/* return: a tuple or NULL */
-ExecUnique(Unique *node)
+ExecUnique(UniqueState *node)
 {
-	UniqueState *uniquestate;
+	Unique	   *plannode = (Unique *) node->ps.plan;
 	TupleTableSlot *resultTupleSlot;
 	TupleTableSlot *slot;
-	Plan	   *outerPlan;
+	PlanState  *outerPlan;
 	TupleDesc	tupDesc;
 
 	/*
 	 * get information from the node
 	 */
-	uniquestate = node->uniquestate;
-	outerPlan = outerPlan((Plan *) node);
-	resultTupleSlot = uniquestate->cstate.cs_ResultTupleSlot;
-	tupDesc = ExecGetResultType(&uniquestate->cstate);
+	outerPlan = outerPlanState(node);
+	resultTupleSlot = node->ps.ps_ResultTupleSlot;
+	tupDesc = ExecGetResultType(&node->ps);
 
 	/*
 	 * now loop, returning only non-duplicate tuples. We assume that the
@@ -64,14 +62,14 @@ ExecUnique(Unique *node)
 		/*
 		 * fetch a tuple from the outer subplan
 		 */
-		slot = ExecProcNode(outerPlan, (Plan *) node);
+		slot = ExecProcNode(outerPlan);
 		if (TupIsNull(slot))
 			return NULL;
 
 		/*
 		 * Always return the first tuple from the subplan.
 		 */
-		if (uniquestate->priorTuple == NULL)
+		if (node->priorTuple == NULL)
 			break;
 
 		/*
@@ -79,11 +77,11 @@ ExecUnique(Unique *node)
 		 * match.  If so then we loop back and fetch another new tuple
 		 * from the subplan.
 		 */
-		if (!execTuplesMatch(slot->val, uniquestate->priorTuple,
+		if (!execTuplesMatch(slot->val, node->priorTuple,
 							 tupDesc,
-							 node->numCols, node->uniqColIdx,
-							 uniquestate->eqfunctions,
-							 uniquestate->tempContext))
+							 plannode->numCols, plannode->uniqColIdx,
+							 node->eqfunctions,
+							 node->tempContext))
 			break;
 	}
 
@@ -99,11 +97,11 @@ ExecUnique(Unique *node)
 	 * handling in execMain.c).  We assume that the caller will no longer
 	 * be interested in the current tuple after he next calls us.
 	 */
-	if (uniquestate->priorTuple != NULL)
-		heap_freetuple(uniquestate->priorTuple);
-	uniquestate->priorTuple = heap_copytuple(slot->val);
+	if (node->priorTuple != NULL)
+		heap_freetuple(node->priorTuple);
+	node->priorTuple = heap_copytuple(slot->val);
 
-	ExecStoreTuple(uniquestate->priorTuple,
+	ExecStoreTuple(node->priorTuple,
 				   resultTupleSlot,
 				   InvalidBuffer,
 				   false);		/* tuple does not belong to slot */
@@ -118,22 +116,18 @@ ExecUnique(Unique *node)
  *		the node's subplan.
  * ----------------------------------------------------------------
  */
-bool							/* return: initialization status */
-ExecInitUnique(Unique *node, EState *estate, Plan *parent)
+UniqueState *
+ExecInitUnique(Unique *node, EState *estate)
 {
 	UniqueState *uniquestate;
-	Plan	   *outerPlan;
 
 	/*
-	 * assign execution state to node
-	 */
-	node->plan.state = estate;
-
-	/*
-	 * create new UniqueState for node
+	 * create state structure
 	 */
 	uniquestate = makeNode(UniqueState);
-	node->uniquestate = uniquestate;
+	uniquestate->ps.plan = (Plan *) node;
+	uniquestate->ps.state = estate;
+
 	uniquestate->priorTuple = NULL;
 
 	/*
@@ -155,30 +149,29 @@ ExecInitUnique(Unique *node, EState *estate, Plan *parent)
 	/*
 	 * Tuple table initialization
 	 */
-	ExecInitResultTupleSlot(estate, &uniquestate->cstate);
+	ExecInitResultTupleSlot(estate, &uniquestate->ps);
 
 	/*
 	 * then initialize outer plan
 	 */
-	outerPlan = outerPlan((Plan *) node);
-	ExecInitNode(outerPlan, estate, (Plan *) node);
+	outerPlanState(uniquestate) = ExecInitNode(outerPlan(node), estate);
 
 	/*
 	 * unique nodes do no projections, so initialize projection info for
 	 * this node appropriately
 	 */
-	ExecAssignResultTypeFromOuterPlan((Plan *) node, &uniquestate->cstate);
-	uniquestate->cstate.cs_ProjInfo = NULL;
+	ExecAssignResultTypeFromOuterPlan(&uniquestate->ps);
+	uniquestate->ps.ps_ProjInfo = NULL;
 
 	/*
 	 * Precompute fmgr lookup data for inner loop
 	 */
 	uniquestate->eqfunctions =
-		execTuplesMatchPrepare(ExecGetResultType(&uniquestate->cstate),
+		execTuplesMatchPrepare(ExecGetResultType(&uniquestate->ps),
 							   node->numCols,
 							   node->uniqColIdx);
 
-	return TRUE;
+	return uniquestate;
 }
 
 int
@@ -197,41 +190,36 @@ ExecCountSlotsUnique(Unique *node)
  * ----------------------------------------------------------------
  */
 void
-ExecEndUnique(Unique *node)
+ExecEndUnique(UniqueState *node)
 {
-	UniqueState *uniquestate = node->uniquestate;
-
-	ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
-
-	MemoryContextDelete(uniquestate->tempContext);
-
 	/* clean up tuple table */
-	ExecClearTuple(uniquestate->cstate.cs_ResultTupleSlot);
-	if (uniquestate->priorTuple != NULL)
+	ExecClearTuple(node->ps.ps_ResultTupleSlot);
+	if (node->priorTuple != NULL)
 	{
-		heap_freetuple(uniquestate->priorTuple);
-		uniquestate->priorTuple = NULL;
+		heap_freetuple(node->priorTuple);
+		node->priorTuple = NULL;
 	}
+
+	ExecEndNode(outerPlanState(node));
+
+	MemoryContextDelete(node->tempContext);
 }
 
 
 void
-ExecReScanUnique(Unique *node, ExprContext *exprCtxt, Plan *parent)
+ExecReScanUnique(UniqueState *node, ExprContext *exprCtxt)
 {
-	UniqueState *uniquestate = node->uniquestate;
-
-	ExecClearTuple(uniquestate->cstate.cs_ResultTupleSlot);
-	if (uniquestate->priorTuple != NULL)
+	ExecClearTuple(node->ps.ps_ResultTupleSlot);
+	if (node->priorTuple != NULL)
 	{
-		heap_freetuple(uniquestate->priorTuple);
-		uniquestate->priorTuple = NULL;
+		heap_freetuple(node->priorTuple);
+		node->priorTuple = NULL;
 	}
 
 	/*
 	 * if chgParam of subnode is not null then plan will be re-scanned by
 	 * first ExecProcNode.
 	 */
-	if (((Plan *) node)->lefttree->chgParam == NULL)
-		ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
-
+	if (((PlanState *) node)->lefttree->chgParam == NULL)
+		ExecReScan(((PlanState *) node)->lefttree, exprCtxt);
 }
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 6e897e423eb..147becf6ed8 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.78 2002/11/13 00:39:47 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.79 2002/12/05 15:50:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,7 +33,7 @@ static int	_SPI_connected = -1;
 static int	_SPI_curid = -1;
 
 static int	_SPI_execute(char *src, int tcount, _SPI_plan *plan);
-static int	_SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount);
+static int	_SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount);
 
 static int _SPI_execute_plan(_SPI_plan *plan,
 				  Datum *Values, char *Nulls, int tcount);
@@ -705,9 +705,8 @@ SPI_cursor_open(char *name, void *plan, Datum *Values, char *Nulls)
 	List	   *ptlist = spiplan->ptlist;
 	Query	   *queryTree;
 	Plan	   *planTree;
+	ParamListInfo paramLI;
 	QueryDesc  *queryDesc;
-	EState	   *eState;
-	TupleDesc	attinfo;
 	MemoryContext oldcontext;
 	Portal		portal;
 	char		portalname[64];
@@ -774,28 +773,21 @@ SPI_cursor_open(char *name, void *plan, Datum *Values, char *Nulls)
 	queryTree->into->relname = pstrdup(name);
 	queryTree->isBinary = false;
 
-	/* Create the QueryDesc object and the executor state */
-	queryDesc = CreateQueryDesc(queryTree, planTree, SPI, NULL);
-	eState = CreateExecutorState();
-
-	/* If the plan has parameters, put them into the executor state */
+	/* If the plan has parameters, set them up */
 	if (spiplan->nargs > 0)
 	{
-		ParamListInfo paramLI;
-
 		paramLI = (ParamListInfo) palloc0((spiplan->nargs + 1) *
-										 sizeof(ParamListInfoData));
+										  sizeof(ParamListInfoData));
 
-		eState->es_param_list_info = paramLI;
-		for (k = 0; k < spiplan->nargs; paramLI++, k++)
+		for (k = 0; k < spiplan->nargs; k++)
 		{
-			paramLI->kind = PARAM_NUM;
-			paramLI->id = k + 1;
-			paramLI->isnull = (Nulls && Nulls[k] == 'n');
-			if (paramLI->isnull)
+			paramLI[k].kind = PARAM_NUM;
+			paramLI[k].id = k + 1;
+			paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
+			if (paramLI[k].isnull)
 			{
 				/* nulls just copy */
-				paramLI->value = Values[k];
+				paramLI[k].value = Values[k];
 			}
 			else
 			{
@@ -805,20 +797,24 @@ SPI_cursor_open(char *name, void *plan, Datum *Values, char *Nulls)
 
 				get_typlenbyval(spiplan->argtypes[k],
 								&paramTypLen, &paramTypByVal);
-				paramLI->value = datumCopy(Values[k],
-										   paramTypByVal, paramTypLen);
+				paramLI[k].value = datumCopy(Values[k],
+											 paramTypByVal, paramTypLen);
 			}
 		}
-		paramLI->kind = PARAM_INVALID;
+		paramLI[k].kind = PARAM_INVALID;
 	}
 	else
-		eState->es_param_list_info = NULL;
+		paramLI = NULL;
+
+	/* Create the QueryDesc object */
+	queryDesc = CreateQueryDesc(queryTree, planTree, SPI, NULL,
+								paramLI, false);
 
 	/* Start the executor */
-	attinfo = ExecutorStart(queryDesc, eState);
+	ExecutorStart(queryDesc);
 
-	/* Put all the objects into the portal */
-	PortalSetQuery(portal, queryDesc, attinfo, eState, PortalCleanup);
+	/* Arrange to shut down the executor if portal is dropped */
+	PortalSetQuery(portal, queryDesc, PortalCleanup);
 
 	/* Switch back to the callers memory context */
 	MemoryContextSwitchTo(oldcontext);
@@ -1042,7 +1038,6 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan)
 			Plan	   *planTree;
 			bool		canSetResult;
 			QueryDesc  *qdesc;
-			EState	   *state;
 
 			planTree = pg_plan_query(queryTree);
 			plan_list = lappend(plan_list, planTree);
@@ -1089,9 +1084,9 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan)
 			else if (plan == NULL)
 			{
 				qdesc = CreateQueryDesc(queryTree, planTree,
-										canSetResult ? SPI : None, NULL);
-				state = CreateExecutorState();
-				res = _SPI_pquery(qdesc, state, canSetResult ? tcount : 0);
+										canSetResult ? SPI : None,
+										NULL, NULL, false);
+				res = _SPI_pquery(qdesc, true, canSetResult ? tcount : 0);
 				if (res < 0)
 					return res;
 				CommandCounterIncrement();
@@ -1099,8 +1094,9 @@ _SPI_execute(char *src, int tcount, _SPI_plan *plan)
 			else
 			{
 				qdesc = CreateQueryDesc(queryTree, planTree,
-										canSetResult ? SPI : None, NULL);
-				res = _SPI_pquery(qdesc, NULL, 0);
+										canSetResult ? SPI : None,
+										NULL, NULL, false);
+				res = _SPI_pquery(qdesc, false, 0);
 				if (res < 0)
 					return res;
 			}
@@ -1152,7 +1148,6 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, char *Nulls, int tcount)
 			Plan	   *planTree;
 			bool		canSetResult;
 			QueryDesc  *qdesc;
-			EState	   *state;
 
 			planTree = lfirst(plan_list);
 			plan_list = lnext(plan_list);
@@ -1183,30 +1178,31 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, char *Nulls, int tcount)
 			}
 			else
 			{
-				qdesc = CreateQueryDesc(queryTree, planTree,
-										canSetResult ? SPI : None, NULL);
-				state = CreateExecutorState();
+				ParamListInfo paramLI;
+
 				if (nargs > 0)
 				{
-					ParamListInfo paramLI;
 					int			k;
 
 					paramLI = (ParamListInfo)
 						palloc0((nargs + 1) * sizeof(ParamListInfoData));
 
-					state->es_param_list_info = paramLI;
-					for (k = 0; k < plan->nargs; paramLI++, k++)
+					for (k = 0; k < plan->nargs; k++)
 					{
-						paramLI->kind = PARAM_NUM;
-						paramLI->id = k + 1;
-						paramLI->isnull = (Nulls && Nulls[k] == 'n');
-						paramLI->value = Values[k];
+						paramLI[k].kind = PARAM_NUM;
+						paramLI[k].id = k + 1;
+						paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
+						paramLI[k].value = Values[k];
 					}
-					paramLI->kind = PARAM_INVALID;
+					paramLI[k].kind = PARAM_INVALID;
 				}
 				else
-					state->es_param_list_info = NULL;
-				res = _SPI_pquery(qdesc, state, canSetResult ? tcount : 0);
+					paramLI = NULL;
+
+				qdesc = CreateQueryDesc(queryTree, planTree,
+										canSetResult ? SPI : None,
+										NULL, paramLI, false);
+				res = _SPI_pquery(qdesc, true, canSetResult ? tcount : 0);
 				if (res < 0)
 					return res;
 				CommandCounterIncrement();
@@ -1218,7 +1214,7 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, char *Nulls, int tcount)
 }
 
 static int
-_SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
+_SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount)
 {
 	Query	   *parseTree = queryDesc->parsetree;
 	int			operation = queryDesc->operation;
@@ -1262,7 +1258,7 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
 			return SPI_ERROR_OPUNKNOWN;
 	}
 
-	if (state == NULL)			/* plan preparation, don't execute */
+	if (!runit)					/* plan preparation, don't execute */
 		return res;
 
 #ifdef SPI_EXECUTOR_STATS
@@ -1270,20 +1266,20 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
 		ResetUsage();
 #endif
 
-	ExecutorStart(queryDesc, state);
+	ExecutorStart(queryDesc);
 
 	/*
 	 * Don't work currently --- need to rearrange callers so that we
-	 * prepare the portal before doing CreateExecutorState() etc. See
+	 * prepare the portal before doing ExecutorStart() etc. See
 	 * pquery.c for the correct order of operations.
 	 */
 	if (isRetrieveIntoPortal)
 		elog(FATAL, "SPI_select: retrieve into portal not implemented");
 
-	ExecutorRun(queryDesc, state, ForwardScanDirection, (long) tcount);
+	ExecutorRun(queryDesc, ForwardScanDirection, (long) tcount);
 
-	_SPI_current->processed = state->es_processed;
-	save_lastoid = state->es_lastoid;
+	_SPI_current->processed = queryDesc->estate->es_processed;
+	save_lastoid = queryDesc->estate->es_lastoid;
 
 	if (operation == CMD_SELECT && queryDesc->dest == SPI)
 	{
@@ -1291,7 +1287,7 @@ _SPI_pquery(QueryDesc *queryDesc, EState *state, int tcount)
 			elog(FATAL, "SPI_select: # of processed tuples check failed");
 	}
 
-	ExecutorEnd(queryDesc, state);
+	ExecutorEnd(queryDesc);
 
 #ifdef SPI_EXECUTOR_STATS
 	if (ShowExecutorStats)
@@ -1342,7 +1338,7 @@ _SPI_cursor_operation(Portal portal, bool forward, int count,
 	oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
 
 	querydesc = PortalGetQueryDesc(portal);
-	estate = PortalGetState(portal);
+	estate = querydesc->estate;
 
 	/* Save the queries command destination and set it to SPI (for fetch) */
 	/* or None (for move) */
@@ -1357,7 +1353,7 @@ _SPI_cursor_operation(Portal portal, bool forward, int count,
 		else
 			direction = ForwardScanDirection;
 
-		ExecutorRun(querydesc, estate, direction, (long) count);
+		ExecutorRun(querydesc, direction, (long) count);
 
 		if (estate->es_processed > 0)
 			portal->atStart = false;	/* OK to back up now */
@@ -1371,7 +1367,7 @@ _SPI_cursor_operation(Portal portal, bool forward, int count,
 		else
 			direction = BackwardScanDirection;
 
-		ExecutorRun(querydesc, estate, direction, (long) count);
+		ExecutorRun(querydesc, direction, (long) count);
 
 		if (estate->es_processed > 0)
 			portal->atEnd = false;		/* OK to go forward now */
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index d11b5ed201f..1fe7efd1636 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3,27 +3,28 @@
  * copyfuncs.c
  *	  Copy functions for Postgres tree nodes.
  *
- * NOTE: a general convention when copying or comparing plan nodes is
- * that we ignore the executor state subnode.  We do not need to look
- * at it because no current uses of copyObject() or equal() need to
- * deal with already-executing plan trees.	By leaving the state subnodes
- * out, we avoid needing to write copy/compare routines for all the
- * different executor state node types.
+ * NOTE: we currently support copying all node types found in parse and
+ * plan trees.  We do not support copying executor state trees; there
+ * is no need for that, and no point in maintaining all the code that
+ * would be needed.  We also do not support copying Path trees, mainly
+ * because the circular linkages between RelOptInfo and Path nodes can't
+ * be handled easily in a simple depth-first traversal.
  *
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.225 2002/11/30 05:21:01 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.226 2002/12/05 15:50:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
-#include "optimizer/clauses.h"
-#include "optimizer/planmain.h"
+#include "nodes/parsenodes.h"
+#include "nodes/plannodes.h"
+#include "nodes/relation.h"
 #include "utils/datum.h"
 
 
@@ -58,15 +59,6 @@
 		memcpy(newnode->fldname, from->fldname, _size); \
 	} while (0)
 
-/* Special hack for fixing subplan lists of Plan nodes (ick) */
-#define FIX_SUBPLAN_LINKS(subplanfldname, fldname) \
-	do { \
-		if (from->subplanfldname != NIL) \
-			newnode->subplanfldname = \
-				nconc(newnode->subplanfldname, \
-					  pull_subplans((Node *) (newnode->fldname))); \
-	} while (0)
-
 
 /*
  * listCopy
@@ -119,19 +111,13 @@ CopyPlanFields(Plan *from, Plan *newnode)
 	COPY_SCALAR_FIELD(total_cost);
 	COPY_SCALAR_FIELD(plan_rows);
 	COPY_SCALAR_FIELD(plan_width);
-	/* execution state is NOT copied */
 	COPY_NODE_FIELD(targetlist);
 	COPY_NODE_FIELD(qual);
 	COPY_NODE_FIELD(lefttree);
 	COPY_NODE_FIELD(righttree);
+	COPY_NODE_FIELD(initPlan);
 	COPY_INTLIST_FIELD(extParam);
 	COPY_INTLIST_FIELD(locParam);
-	COPY_INTLIST_FIELD(chgParam);
-	COPY_NODE_FIELD(initPlan);
-	/* subPlan list must point to subplans in the new subtree, not the old */
-	newnode->subPlan = NIL;
-	FIX_SUBPLAN_LINKS(subPlan, targetlist);
-	FIX_SUBPLAN_LINKS(subPlan, qual);
 	COPY_SCALAR_FIELD(nParamExec);
 }
 
@@ -170,9 +156,6 @@ _copyResult(Result *from)
 	 */
 	COPY_NODE_FIELD(resconstantqual);
 
-	/* subPlan list must point to subplans in the new subtree, not the old */
-	FIX_SUBPLAN_LINKS(plan.subPlan, resconstantqual);
-
 	return newnode;
 }
 
@@ -266,10 +249,6 @@ _copyIndexScan(IndexScan *from)
 	COPY_NODE_FIELD(indxqualorig);
 	COPY_SCALAR_FIELD(indxorderdir);
 
-	/* subPlan list must point to subplans in the new subtree, not the old */
-	FIX_SUBPLAN_LINKS(scan.plan.subPlan, indxqual);
-	FIX_SUBPLAN_LINKS(scan.plan.subPlan, indxqualorig);
-
 	return newnode;
 }
 
@@ -289,12 +268,8 @@ _copyTidScan(TidScan *from)
 	/*
 	 * copy remainder of node
 	 */
-	COPY_SCALAR_FIELD(needRescan);
 	COPY_NODE_FIELD(tideval);
 
-	/* subPlan list must point to subplans in the new subtree, not the old */
-	FIX_SUBPLAN_LINKS(scan.plan.subPlan, tideval);
-
 	return newnode;
 }
 
@@ -348,9 +323,6 @@ CopyJoinFields(Join *from, Join *newnode)
 
 	COPY_SCALAR_FIELD(jointype);
 	COPY_NODE_FIELD(joinqual);
-
-	/* subPlan list must point to subplans in the new subtree, not the old */
-	FIX_SUBPLAN_LINKS(plan.subPlan, joinqual);
 }
 
 
@@ -406,9 +378,6 @@ _copyMergeJoin(MergeJoin *from)
 	 */
 	COPY_NODE_FIELD(mergeclauses);
 
-	/* subPlan list must point to subplans in the new subtree, not the old */
-	FIX_SUBPLAN_LINKS(join.plan.subPlan, mergeclauses);
-
 	return newnode;
 }
 
@@ -430,9 +399,6 @@ _copyHashJoin(HashJoin *from)
 	 */
 	COPY_NODE_FIELD(hashclauses);
 
-	/* subPlan list must point to subplans in the new subtree, not the old */
-	FIX_SUBPLAN_LINKS(join.plan.subPlan, hashclauses);
-
 	return newnode;
 }
 
@@ -531,12 +497,12 @@ _copyUnique(Unique *from)
 }
 
 /*
- * _copySetOp
+ * _copyHash
  */
-static SetOp *
-_copySetOp(SetOp *from)
+static Hash *
+_copyHash(Hash *from)
 {
-	SetOp	   *newnode = makeNode(SetOp);
+	Hash	   *newnode = makeNode(Hash);
 
 	/*
 	 * copy node superclass fields
@@ -546,21 +512,18 @@ _copySetOp(SetOp *from)
 	/*
 	 * copy remainder of node
 	 */
-	COPY_SCALAR_FIELD(cmd);
-	COPY_SCALAR_FIELD(numCols);
-	COPY_POINTER_FIELD(dupColIdx, from->numCols * sizeof(AttrNumber));
-	COPY_SCALAR_FIELD(flagColIdx);
+	COPY_NODE_FIELD(hashkeys);
 
 	return newnode;
 }
 
 /*
- * _copyLimit
+ * _copySetOp
  */
-static Limit *
-_copyLimit(Limit *from)
+static SetOp *
+_copySetOp(SetOp *from)
 {
-	Limit	   *newnode = makeNode(Limit);
+	SetOp	   *newnode = makeNode(SetOp);
 
 	/*
 	 * copy node superclass fields
@@ -570,19 +533,21 @@ _copyLimit(Limit *from)
 	/*
 	 * copy remainder of node
 	 */
-	COPY_NODE_FIELD(limitOffset);
-	COPY_NODE_FIELD(limitCount);
+	COPY_SCALAR_FIELD(cmd);
+	COPY_SCALAR_FIELD(numCols);
+	COPY_POINTER_FIELD(dupColIdx, from->numCols * sizeof(AttrNumber));
+	COPY_SCALAR_FIELD(flagColIdx);
 
 	return newnode;
 }
 
 /*
- * _copyHash
+ * _copyLimit
  */
-static Hash *
-_copyHash(Hash *from)
+static Limit *
+_copyLimit(Limit *from)
 {
-	Hash	   *newnode = makeNode(Hash);
+	Limit	   *newnode = makeNode(Limit);
 
 	/*
 	 * copy node superclass fields
@@ -592,9 +557,8 @@ _copyHash(Hash *from)
 	/*
 	 * copy remainder of node
 	 */
-	COPY_NODE_FIELD(hashkeys);
-
-	/* XXX could the hashkeys contain subplans?  Not at present... */
+	COPY_NODE_FIELD(limitOffset);
+	COPY_NODE_FIELD(limitCount);
 
 	return newnode;
 }
@@ -611,10 +575,6 @@ _copySubPlan(SubPlan *from)
 	COPY_INTLIST_FIELD(parParam);
 	COPY_NODE_FIELD(sublink);
 
-	/* do not copy execution state */
-	newnode->needShutdown = false;
-	newnode->curTuple = NULL;
-
 	return newnode;
 }
 
@@ -933,313 +893,11 @@ _copyArrayRef(ArrayRef *from)
 /* ****************************************************************
  *						relation.h copy functions
  *
- * XXX the code to copy RelOptInfo and Path nodes is really completely bogus,
- * because it makes no attempt to deal with multiple links from RelOptInfo
- * to Paths, nor with back-links from Paths to their parent RelOptInfo.
- * Currently, since we never actually try to copy a RelOptInfo, this is okay.
+ * We don't support copying RelOptInfo, IndexOptInfo, or Path nodes.
+ * There are some subsidiary structs that are useful to copy, though.
  * ****************************************************************
  */
 
-/*
- * _copyRelOptInfo
- */
-static RelOptInfo *
-_copyRelOptInfo(RelOptInfo *from)
-{
-	RelOptInfo *newnode = makeNode(RelOptInfo);
-
-	COPY_SCALAR_FIELD(reloptkind);
-	COPY_INTLIST_FIELD(relids);
-	COPY_SCALAR_FIELD(rows);
-	COPY_SCALAR_FIELD(width);
-	COPY_NODE_FIELD(targetlist);
-	COPY_NODE_FIELD(pathlist);
-	/* XXX cheapest-path fields should point to members of pathlist? */
-	COPY_NODE_FIELD(cheapest_startup_path);
-	COPY_NODE_FIELD(cheapest_total_path);
-	COPY_SCALAR_FIELD(pruneable);
-	COPY_SCALAR_FIELD(rtekind);
-	COPY_NODE_FIELD(indexlist);
-	COPY_SCALAR_FIELD(pages);
-	COPY_SCALAR_FIELD(tuples);
-	COPY_NODE_FIELD(subplan);
-	COPY_SCALAR_FIELD(joinrti);
-	COPY_INTLIST_FIELD(joinrteids);
-	COPY_NODE_FIELD(baserestrictinfo);
-	COPY_SCALAR_FIELD(baserestrictcost);
-	COPY_INTLIST_FIELD(outerjoinset);
-	COPY_NODE_FIELD(joininfo);
-	COPY_INTLIST_FIELD(index_outer_relids);
-	COPY_NODE_FIELD(index_inner_paths);
-
-	return newnode;
-}
-
-/*
- * _copyIndexOptInfo
- */
-static IndexOptInfo *
-_copyIndexOptInfo(IndexOptInfo *from)
-{
-	IndexOptInfo *newnode = makeNode(IndexOptInfo);
-
-	COPY_SCALAR_FIELD(indexoid);
-	COPY_SCALAR_FIELD(pages);
-	COPY_SCALAR_FIELD(tuples);
-	COPY_SCALAR_FIELD(ncolumns);
-	COPY_SCALAR_FIELD(nkeys);
-
-	if (from->classlist)
-	{
-		/* copy the trailing zero too */
-		COPY_POINTER_FIELD(classlist, (from->ncolumns + 1) * sizeof(Oid));
-	}
-
-	if (from->indexkeys)
-	{
-		/* copy the trailing zero too */
-		COPY_POINTER_FIELD(indexkeys, (from->nkeys + 1) * sizeof(int));
-	}
-
-	if (from->ordering)
-	{
-		/* copy the trailing zero too */
-		COPY_POINTER_FIELD(ordering, (from->ncolumns + 1) * sizeof(Oid));
-	}
-
-	COPY_SCALAR_FIELD(relam);
-	COPY_SCALAR_FIELD(amcostestimate);
-	COPY_SCALAR_FIELD(indproc);
-	COPY_NODE_FIELD(indpred);
-	COPY_SCALAR_FIELD(unique);
-	COPY_INTLIST_FIELD(outer_relids);
-	COPY_NODE_FIELD(inner_paths);
-
-	return newnode;
-}
-
-/*
- * CopyPathFields
- *
- *		This function copies the fields of the Path node.  It is used by
- *		all the copy functions for classes which inherit from Path.
- */
-static void
-CopyPathFields(Path *from, Path *newnode)
-{
-	/*
-	 * Modify the next line, since it causes the copying to cycle (i.e.
-	 * the parent points right back here! -- JMH, 7/7/92. Old version:
-	 * COPY_NODE_FIELD(parent);
-	 */
-	COPY_SCALAR_FIELD(parent);
-
-	COPY_SCALAR_FIELD(startup_cost);
-	COPY_SCALAR_FIELD(total_cost);
-	COPY_SCALAR_FIELD(pathtype);
-	COPY_NODE_FIELD(pathkeys);
-}
-
-/*
- * _copyPath
- */
-static Path *
-_copyPath(Path *from)
-{
-	Path	   *newnode = makeNode(Path);
-
-	CopyPathFields(from, newnode);
-
-	return newnode;
-}
-
-/*
- * _copyIndexPath
- */
-static IndexPath *
-_copyIndexPath(IndexPath *from)
-{
-	IndexPath  *newnode = makeNode(IndexPath);
-
-	/*
-	 * copy node superclass fields
-	 */
-	CopyPathFields((Path *) from, (Path *) newnode);
-
-	/*
-	 * copy remainder of node
-	 */
-	COPY_NODE_FIELD(indexinfo);
-	COPY_NODE_FIELD(indexqual);
-	COPY_SCALAR_FIELD(indexscandir);
-	COPY_SCALAR_FIELD(rows);
-
-	return newnode;
-}
-
-/*
- * _copyTidPath
- */
-static TidPath *
-_copyTidPath(TidPath *from)
-{
-	TidPath    *newnode = makeNode(TidPath);
-
-	/*
-	 * copy node superclass fields
-	 */
-	CopyPathFields((Path *) from, (Path *) newnode);
-
-	/*
-	 * copy remainder of node
-	 */
-	COPY_NODE_FIELD(tideval);
-	COPY_INTLIST_FIELD(unjoined_relids);
-
-	return newnode;
-}
-
-/*
- * _copyAppendPath
- */
-static AppendPath *
-_copyAppendPath(AppendPath *from)
-{
-	AppendPath *newnode = makeNode(AppendPath);
-
-	/*
-	 * copy node superclass fields
-	 */
-	CopyPathFields((Path *) from, (Path *) newnode);
-
-	/*
-	 * copy remainder of node
-	 */
-	COPY_NODE_FIELD(subpaths);
-
-	return newnode;
-}
-
-/*
- * _copyResultPath
- */
-static ResultPath *
-_copyResultPath(ResultPath *from)
-{
-	ResultPath    *newnode = makeNode(ResultPath);
-
-	/*
-	 * copy node superclass fields
-	 */
-	CopyPathFields((Path *) from, (Path *) newnode);
-
-	/*
-	 * copy remainder of node
-	 */
-	COPY_NODE_FIELD(subpath);
-	COPY_NODE_FIELD(constantqual);
-
-	return newnode;
-}
-
-/*
- * _copyMaterialPath
- */
-static MaterialPath *
-_copyMaterialPath(MaterialPath *from)
-{
-	MaterialPath    *newnode = makeNode(MaterialPath);
-
-	/*
-	 * copy node superclass fields
-	 */
-	CopyPathFields((Path *) from, (Path *) newnode);
-
-	/*
-	 * copy remainder of node
-	 */
-	COPY_NODE_FIELD(subpath);
-
-	return newnode;
-}
-
-/*
- * CopyJoinPathFields
- *
- *		This function copies the fields of the JoinPath node.  It is used by
- *		all the copy functions for classes which inherit from JoinPath.
- */
-static void
-CopyJoinPathFields(JoinPath *from, JoinPath *newnode)
-{
-	CopyPathFields((Path *) from, (Path *) newnode);
-
-	COPY_SCALAR_FIELD(jointype);
-	COPY_NODE_FIELD(outerjoinpath);
-	COPY_NODE_FIELD(innerjoinpath);
-	COPY_NODE_FIELD(joinrestrictinfo);
-}
-
-/*
- * _copyNestPath
- */
-static NestPath *
-_copyNestPath(NestPath *from)
-{
-	NestPath   *newnode = makeNode(NestPath);
-
-	/*
-	 * copy node superclass fields
-	 */
-	CopyJoinPathFields((JoinPath *) from, (JoinPath *) newnode);
-
-	return newnode;
-}
-
-/*
- * _copyMergePath
- */
-static MergePath *
-_copyMergePath(MergePath *from)
-{
-	MergePath  *newnode = makeNode(MergePath);
-
-	/*
-	 * copy node superclass fields
-	 */
-	CopyJoinPathFields((JoinPath *) from, (JoinPath *) newnode);
-
-	/*
-	 * copy remainder of node
-	 */
-	COPY_NODE_FIELD(path_mergeclauses);
-	COPY_NODE_FIELD(outersortkeys);
-	COPY_NODE_FIELD(innersortkeys);
-
-	return newnode;
-}
-
-/*
- * _copyHashPath
- */
-static HashPath *
-_copyHashPath(HashPath *from)
-{
-	HashPath   *newnode = makeNode(HashPath);
-
-	/*
-	 * copy node superclass fields
-	 */
-	CopyJoinPathFields((JoinPath *) from, (JoinPath *) newnode);
-
-	/*
-	 * copy remainder of node
-	 */
-	COPY_NODE_FIELD(path_hashclauses);
-
-	return newnode;
-}
-
 /*
  * _copyPathKeyItem
  */
@@ -1301,21 +959,6 @@ _copyJoinInfo(JoinInfo *from)
 	return newnode;
 }
 
-/*
- * _copyInnerIndexscanInfo
- */
-static InnerIndexscanInfo *
-_copyInnerIndexscanInfo(InnerIndexscanInfo *from)
-{
-	InnerIndexscanInfo   *newnode = makeNode(InnerIndexscanInfo);
-
-	COPY_INTLIST_FIELD(other_relids);
-	COPY_SCALAR_FIELD(isouterjoin);
-	COPY_NODE_FIELD(best_innerpath);
-
-	return newnode;
-}
-
 /* ****************************************************************
  *					parsenodes.h copy functions
  * ****************************************************************
@@ -1737,7 +1380,8 @@ _copyQuery(Query *from)
 	/*
 	 * We do not copy the planner internal fields: base_rel_list,
 	 * other_rel_list, join_rel_list, equi_key_list, query_pathkeys,
-	 * hasJoinRTEs.  Not entirely clear if this is right?
+	 * hasJoinRTEs.  That would get us into copying RelOptInfo/Path
+	 * trees, which we don't want to do.
 	 */
 
 	return newnode;
@@ -2683,15 +2327,15 @@ copyObject(void *from)
 		case T_Unique:
 			retval = _copyUnique(from);
 			break;
+		case T_Hash:
+			retval = _copyHash(from);
+			break;
 		case T_SetOp:
 			retval = _copySetOp(from);
 			break;
 		case T_Limit:
 			retval = _copyLimit(from);
 			break;
-		case T_Hash:
-			retval = _copyHash(from);
-			break;
 		case T_SubPlan:
 			retval = _copySubPlan(from);
 			break;
@@ -2757,39 +2401,6 @@ copyObject(void *from)
 			/*
 			 * RELATION NODES
 			 */
-		case T_RelOptInfo:
-			retval = _copyRelOptInfo(from);
-			break;
-		case T_IndexOptInfo:
-			retval = _copyIndexOptInfo(from);
-			break;
-		case T_Path:
-			retval = _copyPath(from);
-			break;
-		case T_IndexPath:
-			retval = _copyIndexPath(from);
-			break;
-		case T_TidPath:
-			retval = _copyTidPath(from);
-			break;
-		case T_AppendPath:
-			retval = _copyAppendPath(from);
-			break;
-		case T_ResultPath:
-			retval = _copyResultPath(from);
-			break;
-		case T_MaterialPath:
-			retval = _copyMaterialPath(from);
-			break;
-		case T_NestPath:
-			retval = _copyNestPath(from);
-			break;
-		case T_MergePath:
-			retval = _copyMergePath(from);
-			break;
-		case T_HashPath:
-			retval = _copyHashPath(from);
-			break;
 		case T_PathKeyItem:
 			retval = _copyPathKeyItem(from);
 			break;
@@ -2799,9 +2410,6 @@ copyObject(void *from)
 		case T_JoinInfo:
 			retval = _copyJoinInfo(from);
 			break;
-		case T_InnerIndexscanInfo:
-			retval = _copyInnerIndexscanInfo(from);
-			break;
 
 			/*
 			 * VALUE NODES
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index c5a492db89c..f2cae4c14f4 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -3,30 +3,30 @@
  * equalfuncs.c
  *	  Equality functions to compare node trees.
  *
- * NOTE: a general convention when copying or comparing plan nodes is
- * that we ignore the executor state subnode.  We do not need to look
- * at it because no current uses of copyObject() or equal() need to
- * deal with already-executing plan trees.	By leaving the state subnodes
- * out, we avoid needing to write copy/compare routines for all the
- * different executor state node types.
+ * NOTE: we currently support comparing all node types found in parse
+ * trees.  We do not support comparing executor state trees; there
+ * is no need for that, and no point in maintaining all the code that
+ * would be needed.  We also do not support comparing Path trees, mainly
+ * because the circular linkages between RelOptInfo and Path nodes can't
+ * be handled easily in a simple depth-first traversal.
  *
- * Currently, in fact, equal() doesn't know how to compare Plan nodes
- * at all, let alone their executor-state subnodes.  This will probably
- * need to be fixed someday, but presently there is no need to compare
- * plan trees.
+ * Currently, in fact, equal() doesn't know how to compare Plan trees
+ * either.  This might need to be fixed someday.
  *
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.170 2002/11/30 05:21:01 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.171 2002/12/05 15:50:34 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
+#include "nodes/params.h"
+#include "nodes/parsenodes.h"
 #include "nodes/plannodes.h"
 #include "nodes/relation.h"
 #include "utils/datum.h"
@@ -369,147 +369,6 @@ _equalSubPlan(SubPlan *a, SubPlan *b)
  * Stuff from relation.h
  */
 
-static bool
-_equalRelOptInfo(RelOptInfo *a, RelOptInfo *b)
-{
-	/*
-	 * We treat RelOptInfos as equal if they refer to the same base rels
-	 * joined in the same order.  Is this appropriate/sufficient?
-	 */
-	COMPARE_INTLIST_FIELD(relids);
-
-	return true;
-}
-
-static bool
-_equalIndexOptInfo(IndexOptInfo *a, IndexOptInfo *b)
-{
-	/*
-	 * We treat IndexOptInfos as equal if they refer to the same index. Is
-	 * this sufficient?
-	 */
-	COMPARE_SCALAR_FIELD(indexoid);
-
-	return true;
-}
-
-static bool
-_equalPath(Path *a, Path *b)
-{
-	/* This is safe only because _equalRelOptInfo is incomplete... */
-	COMPARE_NODE_FIELD(parent);
-	/*
-	 * do not check path costs, since they may not be set yet, and being
-	 * float values there are roundoff error issues anyway...
-	 */
-	COMPARE_SCALAR_FIELD(pathtype);
-	COMPARE_NODE_FIELD(pathkeys);
-
-	return true;
-}
-
-static bool
-_equalIndexPath(IndexPath *a, IndexPath *b)
-{
-	if (!_equalPath((Path *) a, (Path *) b))
-		return false;
-	COMPARE_NODE_FIELD(indexinfo);
-	COMPARE_NODE_FIELD(indexqual);
-	COMPARE_SCALAR_FIELD(indexscandir);
-
-	/*
-	 * Skip 'rows' because of possibility of floating-point roundoff
-	 * error. It should be derivable from the other fields anyway.
-	 */
-	return true;
-}
-
-static bool
-_equalTidPath(TidPath *a, TidPath *b)
-{
-	if (!_equalPath((Path *) a, (Path *) b))
-		return false;
-	COMPARE_NODE_FIELD(tideval);
-	COMPARE_INTLIST_FIELD(unjoined_relids);
-
-	return true;
-}
-
-static bool
-_equalAppendPath(AppendPath *a, AppendPath *b)
-{
-	if (!_equalPath((Path *) a, (Path *) b))
-		return false;
-	COMPARE_NODE_FIELD(subpaths);
-
-	return true;
-}
-
-static bool
-_equalResultPath(ResultPath *a, ResultPath *b)
-{
-	if (!_equalPath((Path *) a, (Path *) b))
-		return false;
-	COMPARE_NODE_FIELD(subpath);
-	COMPARE_NODE_FIELD(constantqual);
-
-	return true;
-}
-
-static bool
-_equalMaterialPath(MaterialPath *a, MaterialPath *b)
-{
-	if (!_equalPath((Path *) a, (Path *) b))
-		return false;
-	COMPARE_NODE_FIELD(subpath);
-
-	return true;
-}
-
-static bool
-_equalJoinPath(JoinPath *a, JoinPath *b)
-{
-	if (!_equalPath((Path *) a, (Path *) b))
-		return false;
-	COMPARE_SCALAR_FIELD(jointype);
-	COMPARE_NODE_FIELD(outerjoinpath);
-	COMPARE_NODE_FIELD(innerjoinpath);
-	COMPARE_NODE_FIELD(joinrestrictinfo);
-
-	return true;
-}
-
-static bool
-_equalNestPath(NestPath *a, NestPath *b)
-{
-	if (!_equalJoinPath((JoinPath *) a, (JoinPath *) b))
-		return false;
-
-	return true;
-}
-
-static bool
-_equalMergePath(MergePath *a, MergePath *b)
-{
-	if (!_equalJoinPath((JoinPath *) a, (JoinPath *) b))
-		return false;
-	COMPARE_NODE_FIELD(path_mergeclauses);
-	COMPARE_NODE_FIELD(outersortkeys);
-	COMPARE_NODE_FIELD(innersortkeys);
-
-	return true;
-}
-
-static bool
-_equalHashPath(HashPath *a, HashPath *b)
-{
-	if (!_equalJoinPath((JoinPath *) a, (JoinPath *) b))
-		return false;
-	COMPARE_NODE_FIELD(path_hashclauses);
-
-	return true;
-}
-
 static bool
 _equalPathKeyItem(PathKeyItem *a, PathKeyItem *b)
 {
@@ -547,16 +406,6 @@ _equalJoinInfo(JoinInfo *a, JoinInfo *b)
 	return true;
 }
 
-static bool
-_equalInnerIndexscanInfo(InnerIndexscanInfo *a, InnerIndexscanInfo *b)
-{
-	COMPARE_INTLIST_FIELD(other_relids);
-	COMPARE_SCALAR_FIELD(isouterjoin);
-	COMPARE_NODE_FIELD(best_innerpath);
-
-	return true;
-}
-
 
 /*
  * Stuff from parsenodes.h
@@ -1711,39 +1560,6 @@ equal(void *a, void *b)
 			retval = _equalJoinExpr(a, b);
 			break;
 
-		case T_RelOptInfo:
-			retval = _equalRelOptInfo(a, b);
-			break;
-		case T_IndexOptInfo:
-			retval = _equalIndexOptInfo(a, b);
-			break;
-		case T_Path:
-			retval = _equalPath(a, b);
-			break;
-		case T_IndexPath:
-			retval = _equalIndexPath(a, b);
-			break;
-		case T_TidPath:
-			retval = _equalTidPath(a, b);
-			break;
-		case T_AppendPath:
-			retval = _equalAppendPath(a, b);
-			break;
-		case T_ResultPath:
-			retval = _equalResultPath(a, b);
-			break;
-		case T_MaterialPath:
-			retval = _equalMaterialPath(a, b);
-			break;
-		case T_NestPath:
-			retval = _equalNestPath(a, b);
-			break;
-		case T_MergePath:
-			retval = _equalMergePath(a, b);
-			break;
-		case T_HashPath:
-			retval = _equalHashPath(a, b);
-			break;
 		case T_PathKeyItem:
 			retval = _equalPathKeyItem(a, b);
 			break;
@@ -1753,9 +1569,6 @@ equal(void *a, void *b)
 		case T_JoinInfo:
 			retval = _equalJoinInfo(a, b);
 			break;
-		case T_InnerIndexscanInfo:
-			retval = _equalInnerIndexscanInfo(a, b);
-			break;
 
 		case T_List:
 			{
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 3a074339f45..2c254001eec 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,13 +8,13 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.185 2002/11/30 05:21:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.186 2002/12/05 15:50:35 tgl Exp $
  *
  * NOTES
  *	  Every node type that can appear in stored rules' parsetrees *must*
  *	  have an output function defined here (as well as an input function
  *	  in readfuncs.c).  For use in debugging, we also provide output
- *	  functions for nodes that appear in raw parsetrees and plan trees.
+ *	  functions for nodes that appear in raw parsetrees, path, and plan trees.
  *	  These nodes however need not have input functions.
  *
  *-------------------------------------------------------------------------
@@ -24,10 +24,8 @@
 #include <ctype.h>
 
 #include "lib/stringinfo.h"
-#include "nodes/nodes.h"
 #include "nodes/parsenodes.h"
 #include "nodes/plannodes.h"
-#include "nodes/primnodes.h"
 #include "nodes/relation.h"
 #include "parser/parse.h"
 #include "utils/datum.h"
@@ -385,11 +383,9 @@ _outPlanInfo(StringInfo str, Plan *node)
 	WRITE_NODE_FIELD(qual);
 	WRITE_NODE_FIELD(lefttree);
 	WRITE_NODE_FIELD(righttree);
+	WRITE_NODE_FIELD(initPlan);
 	WRITE_INTLIST_FIELD(extParam);
 	WRITE_INTLIST_FIELD(locParam);
-	/* chgParam is execution state too */
-	WRITE_NODE_FIELD(initPlan);
-	/* we don't write subPlan; reader must reconstruct list */
 	WRITE_INT_FIELD(nParamExec);
 }
 
@@ -482,7 +478,6 @@ _outTidScan(StringInfo str, TidScan *node)
 
 	_outScanInfo(str, (Scan *) node);
 
-	WRITE_BOOL_FIELD(needRescan);
 	WRITE_NODE_FIELD(tideval);
 }
 
@@ -930,6 +925,8 @@ _outRangeTblEntry(StringInfo str, RangeTblEntry *node)
 
 /*
  * print the basic stuff of all nodes that inherit from Path
+ *
+ * Note we do NOT print the parent, else we'd be in infinite recursion
  */
 static void
 _outPathInfo(StringInfo str, Path *node)
@@ -986,7 +983,6 @@ _outTidPath(StringInfo str, TidPath *node)
 	_outPathInfo(str, (Path *) node);
 
 	WRITE_NODE_FIELD(tideval);
-	WRITE_INTLIST_FIELD(unjoined_relids);
 }
 
 static void
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index 148bd86b85a..0414fdf2f3f 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.126 2002/11/30 05:21:02 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.127 2002/12/05 15:50:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -602,9 +602,6 @@ create_tidscan_plan(TidPath *best_path, List *tlist, List *scan_clauses)
 							 scan_relid,
 							 best_path->tideval);
 
-	if (best_path->unjoined_relids)
-		scan_plan->needRescan = true;
-
 	copy_path_costsize(&scan_plan->scan.plan, &best_path->path);
 
 	return scan_plan;
@@ -1302,13 +1299,11 @@ make_seqscan(List *qptlist,
 	Plan	   *plan = &node->plan;
 
 	/* cost should be inserted by caller */
-	plan->state = (EState *) NULL;
 	plan->targetlist = qptlist;
 	plan->qual = qpqual;
 	plan->lefttree = NULL;
 	plan->righttree = NULL;
 	node->scanrelid = scanrelid;
-	node->scanstate = (CommonScanState *) NULL;
 
 	return node;
 }
@@ -1326,7 +1321,6 @@ make_indexscan(List *qptlist,
 	Plan	   *plan = &node->scan.plan;
 
 	/* cost should be inserted by caller */
-	plan->state = (EState *) NULL;
 	plan->targetlist = qptlist;
 	plan->qual = qpqual;
 	plan->lefttree = NULL;
@@ -1336,7 +1330,6 @@ make_indexscan(List *qptlist,
 	node->indxqual = indxqual;
 	node->indxqualorig = indxqualorig;
 	node->indxorderdir = indexscandir;
-	node->scan.scanstate = (CommonScanState *) NULL;
 
 	return node;
 }
@@ -1351,16 +1344,12 @@ make_tidscan(List *qptlist,
 	Plan	   *plan = &node->scan.plan;
 
 	/* cost should be inserted by caller */
-	plan->state = (EState *) NULL;
 	plan->targetlist = qptlist;
 	plan->qual = qpqual;
 	plan->lefttree = NULL;
 	plan->righttree = NULL;
 	node->scan.scanrelid = scanrelid;
-	node->tideval = copyObject(tideval);		/* XXX do we really need a
-												 * copy? */
-	node->needRescan = false;
-	node->scan.scanstate = (CommonScanState *) NULL;
+	node->tideval = tideval;
 
 	return node;
 }
@@ -1375,14 +1364,12 @@ make_subqueryscan(List *qptlist,
 	Plan	   *plan = &node->scan.plan;
 
 	copy_plan_costsize(plan, subplan);
-	plan->state = (EState *) NULL;
 	plan->targetlist = qptlist;
 	plan->qual = qpqual;
 	plan->lefttree = NULL;
 	plan->righttree = NULL;
 	node->scan.scanrelid = scanrelid;
 	node->subplan = subplan;
-	node->scan.scanstate = (CommonScanState *) NULL;
 
 	return node;
 }
@@ -1396,13 +1383,11 @@ make_functionscan(List *qptlist,
 	Plan	   *plan = &node->scan.plan;
 
 	/* cost should be inserted by caller */
-	plan->state = (EState *) NULL;
 	plan->targetlist = qptlist;
 	plan->qual = qpqual;
 	plan->lefttree = NULL;
 	plan->righttree = NULL;
 	node->scan.scanrelid = scanrelid;
-	node->scan.scanstate = (CommonScanState *) NULL;
 
 	return node;
 }
@@ -1430,7 +1415,6 @@ make_append(List *appendplans, bool isTarget, List *tlist)
 		if (plan->plan_width < subplan->plan_width)
 			plan->plan_width = subplan->plan_width;
 	}
-	plan->state = (EState *) NULL;
 	plan->targetlist = tlist;
 	plan->qual = NIL;
 	plan->lefttree = NULL;
@@ -1453,7 +1437,6 @@ make_nestloop(List *tlist,
 	Plan	   *plan = &node->join.plan;
 
 	/* cost should be inserted by caller */
-	plan->state = (EState *) NULL;
 	plan->targetlist = tlist;
 	plan->qual = otherclauses;
 	plan->lefttree = lefttree;
@@ -1477,7 +1460,6 @@ make_hashjoin(List *tlist,
 	Plan	   *plan = &node->join.plan;
 
 	/* cost should be inserted by caller */
-	plan->state = (EState *) NULL;
 	plan->targetlist = tlist;
 	plan->qual = otherclauses;
 	plan->lefttree = lefttree;
@@ -1502,7 +1484,6 @@ make_hash(List *tlist, List *hashkeys, Plan *lefttree)
 	 * input plan; this only affects EXPLAIN display not decisions.
 	 */
 	plan->startup_cost = plan->total_cost;
-	plan->state = (EState *) NULL;
 	plan->targetlist = tlist;
 	plan->qual = NULL;
 	plan->lefttree = lefttree;
@@ -1525,7 +1506,6 @@ make_mergejoin(List *tlist,
 	Plan	   *plan = &node->join.plan;
 
 	/* cost should be inserted by caller */
-	plan->state = (EState *) NULL;
 	plan->targetlist = tlist;
 	plan->qual = otherclauses;
 	plan->lefttree = lefttree;
@@ -1557,7 +1537,6 @@ make_sort(Query *root, List *tlist, Plan *lefttree, int keycount)
 			  lefttree->plan_width);
 	plan->startup_cost = sort_path.startup_cost;
 	plan->total_cost = sort_path.total_cost;
-	plan->state = (EState *) NULL;
 	plan->targetlist = tlist;
 	plan->qual = NIL;
 	plan->lefttree = lefttree;
@@ -1646,7 +1625,6 @@ make_material(List *tlist, Plan *lefttree)
 	Plan	   *plan = &node->plan;
 
 	/* cost should be inserted by caller */
-	plan->state = (EState *) NULL;
 	plan->targetlist = tlist;
 	plan->qual = NIL;
 	plan->lefttree = lefttree;
@@ -1690,7 +1668,6 @@ make_agg(Query *root, List *tlist, List *qual,
 	else
 		plan->plan_rows = numGroups;
 
-	plan->state = (EState *) NULL;
 	plan->qual = qual;
 	plan->targetlist = tlist;
 	plan->lefttree = lefttree;
@@ -1726,7 +1703,6 @@ make_group(Query *root,
 	/* One output tuple per estimated result group */
 	plan->plan_rows = numGroups;
 
-	plan->state = (EState *) NULL;
 	plan->qual = NULL;
 	plan->targetlist = tlist;
 	plan->lefttree = lefttree;
@@ -1765,7 +1741,6 @@ make_unique(List *tlist, Plan *lefttree, List *distinctList)
 	 * if he has a better idea.
 	 */
 
-	plan->state = (EState *) NULL;
 	plan->targetlist = tlist;
 	plan->qual = NIL;
 	plan->lefttree = lefttree;
@@ -1824,7 +1799,6 @@ make_setop(SetOpCmd cmd, List *tlist, Plan *lefttree,
 	if (plan->plan_rows < 1)
 		plan->plan_rows = 1;
 
-	plan->state = (EState *) NULL;
 	plan->targetlist = tlist;
 	plan->qual = NIL;
 	plan->lefttree = lefttree;
@@ -1905,7 +1879,6 @@ make_limit(List *tlist, Plan *lefttree,
 		}
 	}
 
-	plan->state = (EState *) NULL;
 	plan->targetlist = tlist;
 	plan->qual = NIL;
 	plan->lefttree = lefttree;
@@ -1935,13 +1908,11 @@ make_result(List *tlist,
 		plan->plan_width = 0;	/* XXX try to be smarter? */
 	}
 
-	plan->state = (EState *) NULL;
 	plan->targetlist = tlist;
 	plan->qual = NIL;
 	plan->lefttree = subplan;
 	plan->righttree = NULL;
 	node->resconstantqual = resconstantqual;
-	node->resstate = NULL;
 
 	return node;
 }
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 3db73e87f0b..b23843a030c 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.83 2002/11/30 21:25:04 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.84 2002/12/05 15:50:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,6 +41,7 @@ typedef struct
 } replace_vars_with_subplan_refs_context;
 
 static void fix_expr_references(Plan *plan, Node *node);
+static bool fix_expr_references_walker(Node *node, void *context);
 static void set_join_references(Join *join, List *rtable);
 static void set_uppernode_references(Plan *plan, Index subvarno);
 static Node *join_references_mutator(Node *node,
@@ -66,8 +67,6 @@ static bool fix_opids_walker(Node *node, void *context);
  *	  for the convenience of the executor.	We update Vars in upper plan nodes
  *	  to refer to the outputs of their subplans, and we compute regproc OIDs
  *	  for operators (ie, we look up the function that implements each op).
- *	  We must also build lists of all the subplan nodes present in each
- *	  plan node's expression trees.
  *
  *	  set_plan_references recursively traverses the whole plan tree.
  *
@@ -81,12 +80,6 @@ set_plan_references(Plan *plan, List *rtable)
 	if (plan == NULL)
 		return;
 
-	/*
-	 * We must rebuild the plan's list of subplan nodes, since we are
-	 * copying/mutating its expression trees.
-	 */
-	plan->subPlan = NIL;
-
 	/*
 	 * Plan-type-specific fixes
 	 */
@@ -107,6 +100,8 @@ set_plan_references(Plan *plan, List *rtable)
 		case T_TidScan:
 			fix_expr_references(plan, (Node *) plan->targetlist);
 			fix_expr_references(plan, (Node *) plan->qual);
+			fix_expr_references(plan,
+								(Node *) ((TidScan *) plan)->tideval);
 			break;
 		case T_SubqueryScan:
 			{
@@ -175,9 +170,9 @@ set_plan_references(Plan *plan, List *rtable)
 			 * unmodified input tuples).  The optimizer is lazy about
 			 * creating really valid targetlists for them.	Best to just
 			 * leave the targetlist alone.	In particular, we do not want
-			 * to pull a subplan list for them, since we will likely end
-			 * up with duplicate list entries for subplans that also
-			 * appear in lower levels of the plan tree!
+			 * to process subplans for them, since we will likely end
+			 * up reprocessing subplans that also appear in lower levels
+			 * of the plan tree!
 			 */
 			break;
 		case T_Agg:
@@ -206,7 +201,7 @@ set_plan_references(Plan *plan, List *rtable)
 			 * Append, like Sort et al, doesn't actually evaluate its
 			 * targetlist or quals, and we haven't bothered to give it its
 			 * own tlist copy.	So, don't fix targetlist/qual. But do
-			 * recurse into subplans.
+			 * recurse into child plans.
 			 */
 			foreach(pl, ((Append *) plan)->appendplans)
 				set_plan_references((Plan *) lfirst(pl), rtable);
@@ -218,24 +213,19 @@ set_plan_references(Plan *plan, List *rtable)
 	}
 
 	/*
-	 * Now recurse into subplans, if any
+	 * Now recurse into child plans and initplans, if any
 	 *
-	 * NOTE: it is essential that we recurse into subplans AFTER we set
+	 * NOTE: it is essential that we recurse into child plans AFTER we set
 	 * subplan references in this plan's tlist and quals.  If we did the
 	 * reference-adjustments bottom-up, then we would fail to match this
 	 * plan's var nodes against the already-modified nodes of the
-	 * subplans.
+	 * children.  Fortunately, that consideration doesn't apply to SubPlan
+	 * nodes; else we'd need two passes over the expression trees.
 	 */
 	set_plan_references(plan->lefttree, rtable);
 	set_plan_references(plan->righttree, rtable);
-	foreach(pl, plan->initPlan)
-	{
-		SubPlan    *sp = (SubPlan *) lfirst(pl);
 
-		Assert(IsA(sp, SubPlan));
-		set_plan_references(sp->plan, sp->rtable);
-	}
-	foreach(pl, plan->subPlan)
+	foreach(pl, plan->initPlan)
 	{
 		SubPlan    *sp = (SubPlan *) lfirst(pl);
 
@@ -249,13 +239,38 @@ set_plan_references(Plan *plan, List *rtable)
  *	  Do final cleanup on expressions (targetlists or quals).
  *
  * This consists of looking up operator opcode info for Oper nodes
- * and adding subplans to the Plan node's list of contained subplans.
+ * and recursively performing set_plan_references on SubPlans.
+ *
+ * The Plan argument is currently unused, but might be needed again someday.
  */
 static void
 fix_expr_references(Plan *plan, Node *node)
 {
-	fix_opids(node);
-	plan->subPlan = nconc(plan->subPlan, pull_subplans(node));
+	/* This tree walk requires no special setup, so away we go... */
+	fix_expr_references_walker(node, NULL);
+}
+
+static bool
+fix_expr_references_walker(Node *node, void *context)
+{
+	if (node == NULL)
+		return false;
+	if (IsA(node, Expr))
+	{
+		Expr   *expr = (Expr *) node;
+
+		if (expr->opType == OP_EXPR ||
+			expr->opType == DISTINCT_EXPR)
+			replace_opid((Oper *) expr->oper);
+		else if (expr->opType == SUBPLAN_EXPR)
+		{
+			SubPlan	   *sp = (SubPlan *) expr->oper;
+
+			Assert(IsA(sp, SubPlan));
+			set_plan_references(sp->plan, sp->rtable);
+		}
+	}
+	return expression_tree_walker(node, fix_expr_references_walker, context);
 }
 
 /*
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index e4bbd29105e..a65de72c90b 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.58 2002/11/30 05:21:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.59 2002/12/05 15:50:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,6 +16,7 @@
 #include "catalog/pg_operator.h"
 #include "catalog/pg_type.h"
 #include "nodes/makefuncs.h"
+#include "nodes/params.h"
 #include "optimizer/clauses.h"
 #include "optimizer/cost.h"
 #include "optimizer/planmain.h"
@@ -297,7 +298,7 @@ make_subplan(SubLink *slink)
 			switch (nodeTag(plan))
 			{
 				case T_SeqScan:
-					if (plan->initPlan || plan->subPlan)
+					if (plan->initPlan)
 						use_material = true;
 					else
 					{
@@ -453,20 +454,13 @@ convert_sublink_opers(SubLink *slink, List *targetlist,
 }
 
 /*
- * finalize_primnode: build lists of subplans and params appearing
- * in the given expression tree.  NOTE: items are added to lists passed in,
- * so caller must initialize lists to NIL before first call!
- *
- * Note: the subplan list that is constructed here and assigned to the
- * plan's subPlan field will be replaced with an up-to-date list in
- * set_plan_references().  We could almost dispense with building this
- * subplan list at all; I believe the only place that uses it is the
- * check in make_subplan to see whether a subselect has any subselects.
+ * finalize_primnode: build lists of params appearing
+ * in the given expression tree.  NOTE: items are added to list passed in,
+ * so caller must initialize list to NIL before first call!
  */
 
 typedef struct finalize_primnode_results
 {
-	List	   *subplans;		/* List of subplans found in expr */
 	List	   *paramids;		/* List of PARAM_EXEC paramids found */
 } finalize_primnode_results;
 
@@ -491,8 +485,6 @@ finalize_primnode(Node *node, finalize_primnode_results *results)
 		SubPlan    *subplan = (SubPlan *) ((Expr *) node)->oper;
 		List	   *lst;
 
-		/* Add subplan to subplans list */
-		results->subplans = lappend(results->subplans, subplan);
 		/* Check extParam list for params to add to paramids */
 		foreach(lst, subplan->plan->extParam)
 		{
@@ -595,18 +587,16 @@ SS_finalize_plan(Plan *plan, List *rtable)
 	if (plan == NULL)
 		return NIL;
 
-	results.subplans = NIL;		/* initialize lists to NIL */
-	results.paramids = NIL;
+	results.paramids = NIL;		/* initialize list to NIL */
 
 	/*
 	 * When we call finalize_primnode, results.paramids lists are
 	 * automatically merged together.  But when recursing to self, we have
 	 * to do it the hard way.  We want the paramids list to include params
-	 * in subplans as well as at this level. (We don't care about finding
-	 * subplans of subplans, though.)
+	 * in subplans as well as at this level.
 	 */
 
-	/* Find params and subplans in targetlist and qual */
+	/* Find params in targetlist and qual */
 	finalize_primnode((Node *) plan->targetlist, &results);
 	finalize_primnode((Node *) plan->qual, &results);
 
@@ -624,8 +614,7 @@ SS_finalize_plan(Plan *plan, List *rtable)
 
 			/*
 			 * we need not look at indxqualorig, since it will have the
-			 * same param references as indxqual, and we aren't really
-			 * concerned yet about having a complete subplan list.
+			 * same param references as indxqual.
 			 */
 			break;
 
@@ -704,7 +693,7 @@ SS_finalize_plan(Plan *plan, List *rtable)
 				 nodeTag(plan));
 	}
 
-	/* Process left and right subplans, if any */
+	/* Process left and right child plans, if any */
 	results.paramids = set_unioni(results.paramids,
 								  SS_finalize_plan(plan->lefttree,
 												   rtable));
@@ -712,7 +701,7 @@ SS_finalize_plan(Plan *plan, List *rtable)
 								  SS_finalize_plan(plan->righttree,
 												   rtable));
 
-	/* Now we have all the paramids and subplans */
+	/* Now we have all the paramids */
 
 	foreach(lst, results.paramids)
 	{
@@ -733,7 +722,6 @@ SS_finalize_plan(Plan *plan, List *rtable)
 
 	plan->extParam = extParam;
 	plan->locParam = locParam;
-	plan->subPlan = results.subplans;
 
 	return results.paramids;
 }
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index 84896b939fd..615d9966973 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.82 2002/11/30 05:21:03 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.83 2002/12/05 15:50:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -385,9 +385,7 @@ create_tidscan_path(Query *root, RelOptInfo *rel, List *tideval)
 	pathnode->path.pathtype = T_TidScan;
 	pathnode->path.parent = rel;
 	pathnode->path.pathkeys = NIL;
-	pathnode->tideval = copyObject(tideval);	/* is copy really
-												 * necessary? */
-	pathnode->unjoined_relids = NIL;
+	pathnode->tideval = tideval;
 
 	cost_tidscan(&pathnode->path, root, rel, tideval);
 
diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c
index 0919b03f45a..24d4aac8b2c 100644
--- a/src/backend/tcop/pquery.c
+++ b/src/backend/tcop/pquery.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.56 2002/11/18 01:17:39 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.57 2002/12/05 15:50:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,15 +23,16 @@
 #include "utils/ps_status.h"
 
 
-/* ----------------------------------------------------------------
- *		CreateQueryDesc
- * ----------------------------------------------------------------
+/*
+ * CreateQueryDesc
  */
 QueryDesc *
 CreateQueryDesc(Query *parsetree,
 				Plan *plantree,
 				CommandDest dest,
-				const char *portalName)
+				const char *portalName,
+				ParamListInfo params,
+				bool doInstrument)
 {
 	QueryDesc  *qd = (QueryDesc *) palloc(sizeof(QueryDesc));
 
@@ -40,54 +41,15 @@ CreateQueryDesc(Query *parsetree,
 	qd->plantree = plantree;	/* plan */
 	qd->dest = dest;			/* output dest */
 	qd->portalName = portalName;	/* name, if dest is a portal */
-	qd->tupDesc = NULL;			/* until set by ExecutorStart */
-
-	return qd;
-}
-
-/* ----------------------------------------------------------------
- *		CreateExecutorState
- *
- *		Note: this may someday take parameters -cim 9/18/89
- * ----------------------------------------------------------------
- */
-EState *
-CreateExecutorState(void)
-{
-	EState	   *state;
-
-	/*
-	 * create a new executor state
-	 */
-	state = makeNode(EState);
-
-	/*
-	 * initialize the Executor State structure
-	 */
-	state->es_direction = ForwardScanDirection;
-	state->es_range_table = NIL;
-
-	state->es_result_relations = NULL;
-	state->es_num_result_relations = 0;
-	state->es_result_relation_info = NULL;
-
-	state->es_junkFilter = NULL;
-
-	state->es_into_relation_descriptor = NULL;
-
-	state->es_param_list_info = NULL;
-	state->es_param_exec_vals = NULL;
-
-	state->es_tupleTable = NULL;
-
-	state->es_query_cxt = CurrentMemoryContext;
+	qd->params = params;		/* parameter values passed into query */
+	qd->doInstrument = doInstrument; /* instrumentation wanted? */
 
-	state->es_per_tuple_exprcontext = NULL;
+	/* null these fields until set by ExecutorStart */
+	qd->tupDesc = NULL;
+	qd->estate = NULL;
+	qd->planstate = NULL;
 
-	/*
-	 * return the executor state structure
-	 */
-	return state;
+	return qd;
 }
 
 /* ----------------
@@ -147,8 +109,6 @@ ProcessQuery(Query *parsetree,
 	Portal		portal = NULL;
 	MemoryContext oldContext = NULL;
 	QueryDesc  *queryDesc;
-	EState	   *state;
-	TupleDesc	attinfo;
 
 	/*
 	 * Check for special-case destinations
@@ -193,7 +153,7 @@ ProcessQuery(Query *parsetree,
 
 		/*
 		 * We stay in portal's memory context for now, so that query desc,
-		 * EState, and plan startup info are also allocated in the portal
+		 * exec state, and plan startup info are also allocated in the portal
 		 * context.
 		 */
 	}
@@ -201,17 +161,12 @@ ProcessQuery(Query *parsetree,
 	/*
 	 * Now we can create the QueryDesc object.
 	 */
-	queryDesc = CreateQueryDesc(parsetree, plan, dest, intoName);
-
-	/*
-	 * create a default executor state.
-	 */
-	state = CreateExecutorState();
+	queryDesc = CreateQueryDesc(parsetree, plan, dest, intoName, NULL, false);
 
 	/*
 	 * call ExecStart to prepare the plan for execution
 	 */
-	attinfo = ExecutorStart(queryDesc, state);
+	ExecutorStart(queryDesc);
 
 	/*
 	 * If retrieve into portal, stop now; we do not run the plan until a
@@ -219,11 +174,8 @@ ProcessQuery(Query *parsetree,
 	 */
 	if (isRetrieveIntoPortal)
 	{
-		PortalSetQuery(portal,
-					   queryDesc,
-					   attinfo,
-					   state,
-					   PortalCleanup);
+		/* Arrange to shut down the executor if portal is dropped */
+		PortalSetQuery(portal, queryDesc, PortalCleanup);
 
 		/* Now we can return to caller's memory context. */
 		MemoryContextSwitchTo(oldContext);
@@ -239,7 +191,7 @@ ProcessQuery(Query *parsetree,
 	 * Now we get to the important call to ExecutorRun() where we actually
 	 * run the plan..
 	 */
-	ExecutorRun(queryDesc, state, ForwardScanDirection, 0L);
+	ExecutorRun(queryDesc, ForwardScanDirection, 0L);
 
 	/*
 	 * Build command completion status string, if caller wants one.
@@ -254,20 +206,20 @@ ProcessQuery(Query *parsetree,
 				strcpy(completionTag, "SELECT");
 				break;
 			case CMD_INSERT:
-				if (state->es_processed == 1)
-					lastOid = state->es_lastoid;
+				if (queryDesc->estate->es_processed == 1)
+					lastOid = queryDesc->estate->es_lastoid;
 				else
 					lastOid = InvalidOid;
 				snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
-						 "INSERT %u %u", lastOid, state->es_processed);
+						 "INSERT %u %u", lastOid, queryDesc->estate->es_processed);
 				break;
 			case CMD_UPDATE:
 				snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
-						 "UPDATE %u", state->es_processed);
+						 "UPDATE %u", queryDesc->estate->es_processed);
 				break;
 			case CMD_DELETE:
 				snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
-						 "DELETE %u", state->es_processed);
+						 "DELETE %u", queryDesc->estate->es_processed);
 				break;
 			default:
 				strcpy(completionTag, "???");
@@ -278,5 +230,5 @@ ProcessQuery(Query *parsetree,
 	/*
 	 * Now, we close down all the scans and free allocated resources.
 	 */
-	ExecutorEnd(queryDesc, state);
+	ExecutorEnd(queryDesc);
 }
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index 4805c4481c5..eaead7dffc9 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.49 2002/06/20 20:29:40 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/mmgr/portalmem.c,v 1.50 2002/12/05 15:50:35 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -149,26 +149,15 @@ GetPortalByName(char *name)
 /*
  * PortalSetQuery
  *		Attaches a "query" to portal.
- *
- * Exceptions:
- *		BadState if called when disabled.
- *		BadArg if portal is invalid.
- *		BadArg if queryDesc is "invalid."
- *		BadArg if state is "invalid."
  */
 void
 PortalSetQuery(Portal portal,
 			   QueryDesc *queryDesc,
-			   TupleDesc attinfo,
-			   EState *state,
 			   void (*cleanup) (Portal portal))
 {
 	AssertArg(PortalIsValid(portal));
-	AssertArg(IsA((Node *) state, EState));
 
 	portal->queryDesc = queryDesc;
-	portal->attinfo = attinfo;
-	portal->state = state;
 	portal->atStart = true;		/* Allow fetch forward only */
 	portal->atEnd = false;
 	portal->cleanup = cleanup;
@@ -212,8 +201,6 @@ CreatePortal(char *name)
 
 	/* initialize portal query */
 	portal->queryDesc = NULL;
-	portal->attinfo = NULL;
-	portal->state = NULL;
 	portal->atStart = true;		/* disallow fetches until query is set */
 	portal->atEnd = true;
 	portal->cleanup = NULL;
diff --git a/src/include/executor/execdesc.h b/src/include/executor/execdesc.h
index 363dabe43b4..9a95551d7c8 100644
--- a/src/include/executor/execdesc.h
+++ b/src/include/executor/execdesc.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execdesc.h,v 1.20 2002/09/04 20:31:42 momjian Exp $
+ * $Id: execdesc.h,v 1.21 2002/12/05 15:50:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,30 +16,38 @@
 #define EXECDESC_H
 
 #include "nodes/parsenodes.h"
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 #include "tcop/dest.h"
 
 
 /* ----------------
  *		query descriptor:
+ *
  *	a QueryDesc encapsulates everything that the executor
  *	needs to execute the query
  * ---------------------
  */
 typedef struct QueryDesc
 {
+	/* These fields are provided by CreateQueryDesc */
 	CmdType		operation;		/* CMD_SELECT, CMD_UPDATE, etc. */
-	Query	   *parsetree;
-	Plan	   *plantree;
+	Query	   *parsetree;		/* rewritten parsetree */
+	Plan	   *plantree;		/* planner's output */
 	CommandDest dest;			/* the destination output of the execution */
 	const char *portalName;		/* name of portal, or NULL */
+	ParamListInfo params;		/* param values being passed in */
+	bool		doInstrument;	/* TRUE requests runtime instrumentation */
 
-	TupleDesc	tupDesc;		/* set by ExecutorStart */
+	/* These fields are set by ExecutorStart */
+	TupleDesc	tupDesc;		/* descriptor for result tuples */
+	EState	   *estate;			/* executor's query-wide state */
+	PlanState  *planstate;		/* tree of per-plan-node state */
 } QueryDesc;
 
 /* in pquery.c */
 extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree,
-				CommandDest dest, const char *portalName);
-
+								  CommandDest dest, const char *portalName,
+								  ParamListInfo params,
+								  bool doInstrument);
 
 #endif   /* EXECDESC_H  */
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index 33b83bce6fe..df3e52d4f0f 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: executor.h,v 1.80 2002/12/01 20:27:32 tgl Exp $
+ * $Id: executor.h,v 1.81 2002/12/05 15:50:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,6 +16,7 @@
 
 #include "executor/execdesc.h"
 
+
 /* ----------------
  *		TupIsNull
  *
@@ -30,9 +31,9 @@
 /*
  * prototypes from functions in execAmi.c
  */
-extern void ExecReScan(Plan *node, ExprContext *exprCtxt, Plan *parent);
-extern void ExecMarkPos(Plan *node);
-extern void ExecRestrPos(Plan *node);
+extern void ExecReScan(PlanState *node, ExprContext *exprCtxt);
+extern void ExecMarkPos(PlanState *node);
+extern void ExecRestrPos(PlanState *node);
 extern bool ExecSupportsMarkRestore(NodeTag plantype);
 
 /*
@@ -49,10 +50,12 @@ extern HeapTuple ExecRemoveJunk(JunkFilter *junkfilter, TupleTableSlot *slot);
 /*
  * prototypes from functions in execMain.c
  */
-extern TupleDesc ExecutorStart(QueryDesc *queryDesc, EState *estate);
-extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, EState *estate,
+extern void ExecutorStart(QueryDesc *queryDesc);
+extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc,
 			ScanDirection direction, long count);
-extern void ExecutorEnd(QueryDesc *queryDesc, EState *estate);
+extern void ExecutorEnd(QueryDesc *queryDesc);
+extern EState *CreateExecutorState(void);
+extern void ExecCheckRTPerms(List *rangeTable, CmdType operation);
 extern void ExecConstraints(const char *caller, ResultRelInfo *resultRelInfo,
 				TupleTableSlot *slot, EState *estate);
 extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti,
@@ -61,11 +64,11 @@ extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti,
 /*
  * prototypes from functions in execProcnode.c
  */
-extern bool ExecInitNode(Plan *node, EState *estate, Plan *parent);
-extern TupleTableSlot *ExecProcNode(Plan *node, Plan *parent);
+extern PlanState *ExecInitNode(Plan *node, EState *estate);
+extern TupleTableSlot *ExecProcNode(PlanState *node);
 extern int	ExecCountSlotsNode(Plan *node);
-extern void ExecEndNode(Plan *node, Plan *parent);
-extern TupleDesc ExecGetTupType(Plan *node);
+extern void ExecEndNode(PlanState *node);
+extern TupleDesc ExecGetTupType(PlanState *node);
 
 /*
  * prototypes from functions in execQual.c
@@ -89,6 +92,7 @@ extern Datum ExecEvalExpr(Node *expression, ExprContext *econtext,
 			 bool *isNull, ExprDoneCond *isDone);
 extern Datum ExecEvalExprSwitchContext(Node *expression, ExprContext *econtext,
 						  bool *isNull, ExprDoneCond *isDone);
+extern Node *ExecInitExpr(Node *node, PlanState *parent);
 extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
@@ -98,9 +102,9 @@ extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
 /*
  * prototypes from functions in execScan.c
  */
-typedef TupleTableSlot *(*ExecScanAccessMtd) (Scan *node);
+typedef TupleTableSlot *(*ExecScanAccessMtd) (ScanState *node);
 
-extern TupleTableSlot *ExecScan(Scan *node, ExecScanAccessMtd accessMtd);
+extern TupleTableSlot *ExecScan(ScanState *node, ExecScanAccessMtd accessMtd);
 
 /*
  * prototypes from functions in execTuples.c
@@ -117,14 +121,13 @@ extern TupleTableSlot *ExecClearTuple(TupleTableSlot *slot);
 extern void ExecSetSlotDescriptor(TupleTableSlot *slot,
 					  TupleDesc tupdesc, bool shouldFree);
 extern void ExecSetSlotDescriptorIsNew(TupleTableSlot *slot, bool isNew);
-extern void ExecInitResultTupleSlot(EState *estate, CommonState *commonstate);
-extern void ExecInitScanTupleSlot(EState *estate,
-					  CommonScanState *commonscanstate);
+extern void ExecInitResultTupleSlot(EState *estate, PlanState *planstate);
+extern void ExecInitScanTupleSlot(EState *estate, ScanState *scanstate);
 extern TupleTableSlot *ExecInitExtraTupleSlot(EState *estate);
 extern TupleTableSlot *ExecInitNullTupleSlot(EState *estate,
 					  TupleDesc tupType);
 extern TupleDesc ExecTypeFromTL(List *targetList, bool hasoid);
-extern void SetChangedParamList(Plan *node, List *newchg);
+extern void SetChangedParamList(PlanState *node, List *newchg);
 
 typedef struct TupOutputState
 {
@@ -155,21 +158,19 @@ extern void end_tup_output(TupOutputState *tstate);
  * prototypes from functions in execUtils.c
  */
 extern void ResetTupleCount(void);
-extern void ExecAssignExprContext(EState *estate, CommonState *commonstate);
-extern void ExecAssignResultType(CommonState *commonstate,
+extern void ExecAssignExprContext(EState *estate, PlanState *planstate);
+extern void ExecAssignResultType(PlanState *planstate,
 					 TupleDesc tupDesc, bool shouldFree);
-extern void ExecAssignResultTypeFromOuterPlan(Plan *node,
-								  CommonState *commonstate);
-extern void ExecAssignResultTypeFromTL(Plan *node, CommonState *commonstate);
-extern TupleDesc ExecGetResultType(CommonState *commonstate);
-extern void ExecAssignProjectionInfo(Plan *node, CommonState *commonstate);
-extern void ExecFreeProjectionInfo(CommonState *commonstate);
-extern void ExecFreeExprContext(CommonState *commonstate);
-extern TupleDesc ExecGetScanType(CommonScanState *csstate);
-extern void ExecAssignScanType(CommonScanState *csstate,
+extern void ExecAssignResultTypeFromOuterPlan(PlanState *planstate);
+extern void ExecAssignResultTypeFromTL(PlanState *planstate);
+extern TupleDesc ExecGetResultType(PlanState *planstate);
+extern void ExecAssignProjectionInfo(PlanState *planstate);
+extern void ExecFreeProjectionInfo(PlanState *planstate);
+extern void ExecFreeExprContext(PlanState *planstate);
+extern TupleDesc ExecGetScanType(ScanState *scanstate);
+extern void ExecAssignScanType(ScanState *scanstate,
 				   TupleDesc tupDesc, bool shouldFree);
-extern void ExecAssignScanTypeFromOuterPlan(Plan *node,
-								CommonScanState *csstate);
+extern void ExecAssignScanTypeFromOuterPlan(ScanState *scanstate);
 
 extern ExprContext *MakeExprContext(TupleTableSlot *slot,
 				MemoryContext queryContext);
diff --git a/src/include/executor/nodeAgg.h b/src/include/executor/nodeAgg.h
index c912d3c9c09..036d67ccaad 100644
--- a/src/include/executor/nodeAgg.h
+++ b/src/include/executor/nodeAgg.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: nodeAgg.h,v 1.17 2002/06/20 20:29:49 momjian Exp $
+ * $Id: nodeAgg.h,v 1.18 2002/12/05 15:50:36 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,13 +15,13 @@
 #define NODEAGG_H
 
 #include "fmgr.h"
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern TupleTableSlot *ExecAgg(Agg *node);
-extern bool ExecInitAgg(Agg *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsAgg(Agg *node);
-extern void ExecEndAgg(Agg *node);
-extern void ExecReScanAgg(Agg *node, ExprContext *exprCtxt, Plan *parent);
+extern AggState *ExecInitAgg(Agg *node, EState *estate);
+extern TupleTableSlot *ExecAgg(AggState *node);
+extern void ExecEndAgg(AggState *node);
+extern void ExecReScanAgg(AggState *node, ExprContext *exprCtxt);
 
 extern Datum aggregate_dummy(PG_FUNCTION_ARGS);
 
diff --git a/src/include/executor/nodeAppend.h b/src/include/executor/nodeAppend.h
index 4e9255b795d..3d18b66d164 100644
--- a/src/include/executor/nodeAppend.h
+++ b/src/include/executor/nodeAppend.h
@@ -7,19 +7,19 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeAppend.h,v 1.17 2002/06/20 20:29:49 momjian Exp $
+ * $Id: nodeAppend.h,v 1.18 2002/12/05 15:50:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef NODEAPPEND_H
 #define NODEAPPEND_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern bool ExecInitAppend(Append *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsAppend(Append *node);
-extern TupleTableSlot *ExecProcAppend(Append *node);
-extern void ExecEndAppend(Append *node);
-extern void ExecReScanAppend(Append *node, ExprContext *exprCtxt, Plan *parent);
+extern AppendState *ExecInitAppend(Append *node, EState *estate);
+extern TupleTableSlot *ExecProcAppend(AppendState *node);
+extern void ExecEndAppend(AppendState *node);
+extern void ExecReScanAppend(AppendState *node, ExprContext *exprCtxt);
 
 #endif   /* NODEAPPEND_H */
diff --git a/src/include/executor/nodeFunctionscan.h b/src/include/executor/nodeFunctionscan.h
index 9a59cd217a4..893d6b097ef 100644
--- a/src/include/executor/nodeFunctionscan.h
+++ b/src/include/executor/nodeFunctionscan.h
@@ -7,21 +7,21 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeFunctionscan.h,v 1.2 2002/06/20 20:29:49 momjian Exp $
+ * $Id: nodeFunctionscan.h,v 1.3 2002/12/05 15:50:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef NODEFUNCTIONSCAN_H
 #define NODEFUNCTIONSCAN_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern TupleTableSlot *ExecFunctionScan(FunctionScan *node);
-extern void ExecEndFunctionScan(FunctionScan *node);
-extern bool ExecInitFunctionScan(FunctionScan *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsFunctionScan(FunctionScan *node);
-extern void ExecFunctionMarkPos(FunctionScan *node);
-extern void ExecFunctionRestrPos(FunctionScan *node);
-extern void ExecFunctionReScan(FunctionScan *node, ExprContext *exprCtxt, Plan *parent);
+extern FunctionScanState *ExecInitFunctionScan(FunctionScan *node, EState *estate);
+extern TupleTableSlot *ExecFunctionScan(FunctionScanState *node);
+extern void ExecEndFunctionScan(FunctionScanState *node);
+extern void ExecFunctionMarkPos(FunctionScanState *node);
+extern void ExecFunctionRestrPos(FunctionScanState *node);
+extern void ExecFunctionReScan(FunctionScanState *node, ExprContext *exprCtxt);
 
 #endif   /* NODEFUNCTIONSCAN_H */
diff --git a/src/include/executor/nodeGroup.h b/src/include/executor/nodeGroup.h
index b3c4e5bbd05..211e55b6cad 100644
--- a/src/include/executor/nodeGroup.h
+++ b/src/include/executor/nodeGroup.h
@@ -7,20 +7,20 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeGroup.h,v 1.22 2002/06/20 20:29:49 momjian Exp $
+ * $Id: nodeGroup.h,v 1.23 2002/12/05 15:50:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef NODEGROUP_H
 #define NODEGROUP_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern TupleTableSlot *ExecGroup(Group *node);
-extern bool ExecInitGroup(Group *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsGroup(Group *node);
-extern void ExecEndGroup(Group *node);
-extern void ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent);
+extern GroupState *ExecInitGroup(Group *node, EState *estate);
+extern TupleTableSlot *ExecGroup(GroupState *node);
+extern void ExecEndGroup(GroupState *node);
+extern void ExecReScanGroup(GroupState *node, ExprContext *exprCtxt);
 
 extern bool execTuplesMatch(HeapTuple tuple1,
 				HeapTuple tuple2,
diff --git a/src/include/executor/nodeHash.h b/src/include/executor/nodeHash.h
index 654906cd3c2..c30073ec8cf 100644
--- a/src/include/executor/nodeHash.h
+++ b/src/include/executor/nodeHash.h
@@ -7,19 +7,21 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeHash.h,v 1.26 2002/11/30 00:08:20 tgl Exp $
+ * $Id: nodeHash.h,v 1.27 2002/12/05 15:50:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef NODEHASH_H
 #define NODEHASH_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern TupleTableSlot *ExecHash(Hash *node);
-extern bool ExecInitHash(Hash *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsHash(Hash *node);
-extern void ExecEndHash(Hash *node);
+extern HashState *ExecInitHash(Hash *node, EState *estate);
+extern TupleTableSlot *ExecHash(HashState *node);
+extern void ExecEndHash(HashState *node);
+extern void ExecReScanHash(HashState *node, ExprContext *exprCtxt);
+
 extern HashJoinTable ExecHashTableCreate(Hash *node);
 extern void ExecHashTableDestroy(HashJoinTable hashtable);
 extern void ExecHashTableInsert(HashJoinTable hashtable,
@@ -31,7 +33,6 @@ extern int ExecHashGetBucket(HashJoinTable hashtable,
 extern HeapTuple ExecScanHashBucket(HashJoinState *hjstate, List *hjclauses,
 				   ExprContext *econtext);
 extern void ExecHashTableReset(HashJoinTable hashtable, long ntuples);
-extern void ExecReScanHash(Hash *node, ExprContext *exprCtxt, Plan *parent);
 extern void ExecChooseHashTableSize(double ntuples, int tupwidth,
 						int *virtualbuckets,
 						int *physicalbuckets,
diff --git a/src/include/executor/nodeHashjoin.h b/src/include/executor/nodeHashjoin.h
index d13a8c9b05b..9233c0758a4 100644
--- a/src/include/executor/nodeHashjoin.h
+++ b/src/include/executor/nodeHashjoin.h
@@ -7,20 +7,21 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeHashjoin.h,v 1.23 2002/06/20 20:29:49 momjian Exp $
+ * $Id: nodeHashjoin.h,v 1.24 2002/12/05 15:50:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef NODEHASHJOIN_H
 #define NODEHASHJOIN_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern TupleTableSlot *ExecHashJoin(HashJoin *node);
-extern bool ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsHashJoin(HashJoin *node);
-extern void ExecEndHashJoin(HashJoin *node);
+extern HashJoinState *ExecInitHashJoin(HashJoin *node, EState *estate);
+extern TupleTableSlot *ExecHashJoin(HashJoinState *node);
+extern void ExecEndHashJoin(HashJoinState *node);
+extern void ExecReScanHashJoin(HashJoinState *node, ExprContext *exprCtxt);
+
 extern void ExecHashJoinSaveTuple(HeapTuple heapTuple, BufFile *file);
-extern void ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent);
 
 #endif   /* NODEHASHJOIN_H */
diff --git a/src/include/executor/nodeIndexscan.h b/src/include/executor/nodeIndexscan.h
index 26596e5fdd4..392807cce05 100644
--- a/src/include/executor/nodeIndexscan.h
+++ b/src/include/executor/nodeIndexscan.h
@@ -7,22 +7,23 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeIndexscan.h,v 1.16 2002/06/20 20:29:49 momjian Exp $
+ * $Id: nodeIndexscan.h,v 1.17 2002/12/05 15:50:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef NODEINDEXSCAN_H
 #define NODEINDEXSCAN_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern TupleTableSlot *ExecIndexScan(IndexScan *node);
-extern void ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent);
-extern void ExecEndIndexScan(IndexScan *node);
-extern void ExecIndexMarkPos(IndexScan *node);
-extern void ExecIndexRestrPos(IndexScan *node);
-extern void ExecUpdateIndexScanKeys(IndexScan *node, ExprContext *econtext);
-extern bool ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsIndexScan(IndexScan *node);
+extern IndexScanState *ExecInitIndexScan(IndexScan *node, EState *estate);
+extern TupleTableSlot *ExecIndexScan(IndexScanState *node);
+extern void ExecEndIndexScan(IndexScanState *node);
+extern void ExecIndexMarkPos(IndexScanState *node);
+extern void ExecIndexRestrPos(IndexScanState *node);
+extern void ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt);
+
+extern void ExecUpdateIndexScanKeys(IndexScanState *node, ExprContext *econtext);
 
 #endif   /* NODEINDEXSCAN_H */
diff --git a/src/include/executor/nodeLimit.h b/src/include/executor/nodeLimit.h
index 16fa6072bee..5b50335ee42 100644
--- a/src/include/executor/nodeLimit.h
+++ b/src/include/executor/nodeLimit.h
@@ -7,19 +7,19 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeLimit.h,v 1.6 2002/06/20 20:29:49 momjian Exp $
+ * $Id: nodeLimit.h,v 1.7 2002/12/05 15:50:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef NODELIMIT_H
 #define NODELIMIT_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern TupleTableSlot *ExecLimit(Limit *node);
-extern bool ExecInitLimit(Limit *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsLimit(Limit *node);
-extern void ExecEndLimit(Limit *node);
-extern void ExecReScanLimit(Limit *node, ExprContext *exprCtxt, Plan *parent);
+extern LimitState *ExecInitLimit(Limit *node, EState *estate);
+extern TupleTableSlot *ExecLimit(LimitState *node);
+extern void ExecEndLimit(LimitState *node);
+extern void ExecReScanLimit(LimitState *node, ExprContext *exprCtxt);
 
 #endif   /* NODELIMIT_H */
diff --git a/src/include/executor/nodeMaterial.h b/src/include/executor/nodeMaterial.h
index 818fe7590ae..8235bc82c83 100644
--- a/src/include/executor/nodeMaterial.h
+++ b/src/include/executor/nodeMaterial.h
@@ -7,21 +7,21 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeMaterial.h,v 1.18 2002/06/20 20:29:49 momjian Exp $
+ * $Id: nodeMaterial.h,v 1.19 2002/12/05 15:50:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef NODEMATERIAL_H
 #define NODEMATERIAL_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern TupleTableSlot *ExecMaterial(Material *node);
-extern bool ExecInitMaterial(Material *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsMaterial(Material *node);
-extern void ExecEndMaterial(Material *node);
-extern void ExecMaterialMarkPos(Material *node);
-extern void ExecMaterialRestrPos(Material *node);
-extern void ExecMaterialReScan(Material *node, ExprContext *exprCtxt, Plan *parent);
+extern MaterialState *ExecInitMaterial(Material *node, EState *estate);
+extern TupleTableSlot *ExecMaterial(MaterialState *node);
+extern void ExecEndMaterial(MaterialState *node);
+extern void ExecMaterialMarkPos(MaterialState *node);
+extern void ExecMaterialRestrPos(MaterialState *node);
+extern void ExecMaterialReScan(MaterialState *node, ExprContext *exprCtxt);
 
 #endif   /* NODEMATERIAL_H */
diff --git a/src/include/executor/nodeMergejoin.h b/src/include/executor/nodeMergejoin.h
index b7ed7cd75b4..1187d6bce29 100644
--- a/src/include/executor/nodeMergejoin.h
+++ b/src/include/executor/nodeMergejoin.h
@@ -7,19 +7,19 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeMergejoin.h,v 1.17 2002/06/20 20:29:49 momjian Exp $
+ * $Id: nodeMergejoin.h,v 1.18 2002/12/05 15:50:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef NODEMERGEJOIN_H
 #define NODEMERGEJOIN_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern TupleTableSlot *ExecMergeJoin(MergeJoin *node);
-extern bool ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsMergeJoin(MergeJoin *node);
-extern void ExecEndMergeJoin(MergeJoin *node);
-extern void ExecReScanMergeJoin(MergeJoin *node, ExprContext *exprCtxt, Plan *parent);
+extern MergeJoinState *ExecInitMergeJoin(MergeJoin *node, EState *estate);
+extern TupleTableSlot *ExecMergeJoin(MergeJoinState *node);
+extern void ExecEndMergeJoin(MergeJoinState *node);
+extern void ExecReScanMergeJoin(MergeJoinState *node, ExprContext *exprCtxt);
 
-#endif   /* NODEMERGEJOIN_H; */
+#endif   /* NODEMERGEJOIN_H */
diff --git a/src/include/executor/nodeNestloop.h b/src/include/executor/nodeNestloop.h
index e6224753edc..fd48820bbd9 100644
--- a/src/include/executor/nodeNestloop.h
+++ b/src/include/executor/nodeNestloop.h
@@ -7,20 +7,19 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeNestloop.h,v 1.18 2002/06/20 20:29:49 momjian Exp $
+ * $Id: nodeNestloop.h,v 1.19 2002/12/05 15:50:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef NODENESTLOOP_H
 #define NODENESTLOOP_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern TupleTableSlot *ExecNestLoop(NestLoop *node);
-extern bool ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsNestLoop(NestLoop *node);
-extern void ExecEndNestLoop(NestLoop *node);
-extern void ExecReScanNestLoop(NestLoop *node, ExprContext *exprCtxt,
-				   Plan *parent);
+extern NestLoopState *ExecInitNestLoop(NestLoop *node, EState *estate);
+extern TupleTableSlot *ExecNestLoop(NestLoopState *node);
+extern void ExecEndNestLoop(NestLoopState *node);
+extern void ExecReScanNestLoop(NestLoopState *node, ExprContext *exprCtxt);
 
 #endif   /* NODENESTLOOP_H */
diff --git a/src/include/executor/nodeResult.h b/src/include/executor/nodeResult.h
index 5d90cda001d..8a446408309 100644
--- a/src/include/executor/nodeResult.h
+++ b/src/include/executor/nodeResult.h
@@ -7,19 +7,19 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeResult.h,v 1.15 2002/06/20 20:29:49 momjian Exp $
+ * $Id: nodeResult.h,v 1.16 2002/12/05 15:50:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef NODERESULT_H
 #define NODERESULT_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern TupleTableSlot *ExecResult(Result *node);
-extern bool ExecInitResult(Result *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsResult(Result *node);
-extern void ExecEndResult(Result *node);
-extern void ExecReScanResult(Result *node, ExprContext *exprCtxt, Plan *parent);
+extern ResultState *ExecInitResult(Result *node, EState *estate);
+extern TupleTableSlot *ExecResult(ResultState *node);
+extern void ExecEndResult(ResultState *node);
+extern void ExecReScanResult(ResultState *node, ExprContext *exprCtxt);
 
 #endif   /* NODERESULT_H */
diff --git a/src/include/executor/nodeSeqscan.h b/src/include/executor/nodeSeqscan.h
index 86d591c88c9..683c4ab7dc9 100644
--- a/src/include/executor/nodeSeqscan.h
+++ b/src/include/executor/nodeSeqscan.h
@@ -7,21 +7,21 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeSeqscan.h,v 1.15 2002/06/20 20:29:49 momjian Exp $
+ * $Id: nodeSeqscan.h,v 1.16 2002/12/05 15:50:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef NODESEQSCAN_H
 #define NODESEQSCAN_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern TupleTableSlot *ExecSeqScan(SeqScan *node);
-extern bool ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsSeqScan(SeqScan *node);
-extern void ExecEndSeqScan(SeqScan *node);
-extern void ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan *parent);
-extern void ExecSeqMarkPos(SeqScan *node);
-extern void ExecSeqRestrPos(SeqScan *node);
+extern SeqScanState *ExecInitSeqScan(SeqScan *node, EState *estate);
+extern TupleTableSlot *ExecSeqScan(SeqScanState *node);
+extern void ExecEndSeqScan(SeqScanState *node);
+extern void ExecSeqMarkPos(SeqScanState *node);
+extern void ExecSeqRestrPos(SeqScanState *node);
+extern void ExecSeqReScan(SeqScanState *node, ExprContext *exprCtxt);
 
 #endif   /* NODESEQSCAN_H */
diff --git a/src/include/executor/nodeSetOp.h b/src/include/executor/nodeSetOp.h
index eb1b8c25189..73b1e7f5fc5 100644
--- a/src/include/executor/nodeSetOp.h
+++ b/src/include/executor/nodeSetOp.h
@@ -7,19 +7,19 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeSetOp.h,v 1.6 2002/06/20 20:29:49 momjian Exp $
+ * $Id: nodeSetOp.h,v 1.7 2002/12/05 15:50:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef NODESETOP_H
 #define NODESETOP_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern TupleTableSlot *ExecSetOp(SetOp *node);
-extern bool ExecInitSetOp(SetOp *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsSetOp(SetOp *node);
-extern void ExecEndSetOp(SetOp *node);
-extern void ExecReScanSetOp(SetOp *node, ExprContext *exprCtxt, Plan *parent);
+extern SetOpState *ExecInitSetOp(SetOp *node, EState *estate);
+extern TupleTableSlot *ExecSetOp(SetOpState *node);
+extern void ExecEndSetOp(SetOpState *node);
+extern void ExecReScanSetOp(SetOpState *node, ExprContext *exprCtxt);
 
 #endif   /* NODESETOP_H */
diff --git a/src/include/executor/nodeSort.h b/src/include/executor/nodeSort.h
index ca2d1d5fb8a..7836b32ab5b 100644
--- a/src/include/executor/nodeSort.h
+++ b/src/include/executor/nodeSort.h
@@ -7,21 +7,21 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeSort.h,v 1.15 2002/06/20 20:29:49 momjian Exp $
+ * $Id: nodeSort.h,v 1.16 2002/12/05 15:50:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef NODESORT_H
 #define NODESORT_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern TupleTableSlot *ExecSort(Sort *node);
-extern bool ExecInitSort(Sort *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsSort(Sort *node);
-extern void ExecEndSort(Sort *node);
-extern void ExecSortMarkPos(Sort *node);
-extern void ExecSortRestrPos(Sort *node);
-extern void ExecReScanSort(Sort *node, ExprContext *exprCtxt, Plan *parent);
+extern SortState *ExecInitSort(Sort *node, EState *estate);
+extern TupleTableSlot *ExecSort(SortState *node);
+extern void ExecEndSort(SortState *node);
+extern void ExecSortMarkPos(SortState *node);
+extern void ExecSortRestrPos(SortState *node);
+extern void ExecReScanSort(SortState *node, ExprContext *exprCtxt);
 
 #endif   /* NODESORT_H */
diff --git a/src/include/executor/nodeSubplan.h b/src/include/executor/nodeSubplan.h
index a061a420258..c573cf900df 100644
--- a/src/include/executor/nodeSubplan.h
+++ b/src/include/executor/nodeSubplan.h
@@ -2,18 +2,26 @@
  *
  * nodeSubplan.h
  *
+ *
+ *
+ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $Id: nodeSubplan.h,v 1.12 2002/12/05 15:50:38 tgl Exp $
+ *
  *-------------------------------------------------------------------------
  */
 #ifndef NODESUBPLAN_H
 #define NODESUBPLAN_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern Datum ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext,
+extern SubPlanState *ExecInitSubPlan(SubPlan *node, EState *estate);
+extern Datum ExecSubPlan(SubPlanState *node, List *pvar, ExprContext *econtext,
 			bool *isNull);
-extern bool ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent);
-extern void ExecReScanSetParamPlan(SubPlan *node, Plan *parent);
-extern void ExecSetParamPlan(SubPlan *node, ExprContext *econtext);
-extern void ExecEndSubPlan(SubPlan *node);
+extern void ExecEndSubPlan(SubPlanState *node);
+extern void ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent);
+
+extern void ExecSetParamPlan(SubPlanState *node, ExprContext *econtext);
 
 #endif   /* NODESUBPLAN_H */
diff --git a/src/include/executor/nodeSubqueryscan.h b/src/include/executor/nodeSubqueryscan.h
index e1a5f01aaf2..5c5d5c88439 100644
--- a/src/include/executor/nodeSubqueryscan.h
+++ b/src/include/executor/nodeSubqueryscan.h
@@ -7,19 +7,19 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeSubqueryscan.h,v 1.6 2002/06/20 20:29:49 momjian Exp $
+ * $Id: nodeSubqueryscan.h,v 1.7 2002/12/05 15:50:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef NODESUBQUERYSCAN_H
 #define NODESUBQUERYSCAN_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern TupleTableSlot *ExecSubqueryScan(SubqueryScan *node);
-extern void ExecEndSubqueryScan(SubqueryScan *node);
-extern bool ExecInitSubqueryScan(SubqueryScan *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsSubqueryScan(SubqueryScan *node);
-extern void ExecSubqueryReScan(SubqueryScan *node, ExprContext *exprCtxt, Plan *parent);
+extern SubqueryScanState *ExecInitSubqueryScan(SubqueryScan *node, EState *estate);
+extern TupleTableSlot *ExecSubqueryScan(SubqueryScanState *node);
+extern void ExecEndSubqueryScan(SubqueryScanState *node);
+extern void ExecSubqueryReScan(SubqueryScanState *node, ExprContext *exprCtxt);
 
 #endif   /* NODESUBQUERYSCAN_H */
diff --git a/src/include/executor/nodeTidscan.h b/src/include/executor/nodeTidscan.h
index 72e6c3e78f2..9aadb592ca1 100644
--- a/src/include/executor/nodeTidscan.h
+++ b/src/include/executor/nodeTidscan.h
@@ -7,22 +7,21 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeTidscan.h,v 1.10 2002/11/30 05:21:03 tgl Exp $
+ * $Id: nodeTidscan.h,v 1.11 2002/12/05 15:50:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef NODETIDSCAN_H
 #define NODETIDSCAN_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern TupleTableSlot *ExecTidScan(TidScan *node);
-extern void ExecTidReScan(TidScan *node, ExprContext *exprCtxt, Plan *parent);
-extern void ExecEndTidScan(TidScan *node);
-extern void ExecTidMarkPos(TidScan *node);
-extern void ExecTidRestrPos(TidScan *node);
-extern bool ExecInitTidScan(TidScan *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsTidScan(TidScan *node);
-extern void ExecTidReScan(TidScan *node, ExprContext *exprCtxt, Plan *parent);
+extern TidScanState *ExecInitTidScan(TidScan *node, EState *estate);
+extern TupleTableSlot *ExecTidScan(TidScanState *node);
+extern void ExecEndTidScan(TidScanState *node);
+extern void ExecTidMarkPos(TidScanState *node);
+extern void ExecTidRestrPos(TidScanState *node);
+extern void ExecTidReScan(TidScanState *node, ExprContext *exprCtxt);
 
 #endif   /* NODETIDSCAN_H */
diff --git a/src/include/executor/nodeUnique.h b/src/include/executor/nodeUnique.h
index f829b653389..d71997e531c 100644
--- a/src/include/executor/nodeUnique.h
+++ b/src/include/executor/nodeUnique.h
@@ -7,19 +7,19 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: nodeUnique.h,v 1.15 2002/06/20 20:29:49 momjian Exp $
+ * $Id: nodeUnique.h,v 1.16 2002/12/05 15:50:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef NODEUNIQUE_H
 #define NODEUNIQUE_H
 
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
-extern TupleTableSlot *ExecUnique(Unique *node);
-extern bool ExecInitUnique(Unique *node, EState *estate, Plan *parent);
 extern int	ExecCountSlotsUnique(Unique *node);
-extern void ExecEndUnique(Unique *node);
-extern void ExecReScanUnique(Unique *node, ExprContext *exprCtxt, Plan *parent);
+extern UniqueState *ExecInitUnique(Unique *node, EState *estate);
+extern TupleTableSlot *ExecUnique(UniqueState *node);
+extern void ExecEndUnique(UniqueState *node);
+extern void ExecReScanUnique(UniqueState *node, ExprContext *exprCtxt);
 
 #endif   /* NODEUNIQUE_H */
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 544510746d6..a4f10ef4c06 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.81 2002/11/30 00:08:20 tgl Exp $
+ * $Id: execnodes.h,v 1.82 2002/12/05 15:50:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,12 +15,11 @@
 #define EXECNODES_H
 
 #include "access/relscan.h"
-#include "access/sdir.h"
 #include "executor/hashjoin.h"
 #include "executor/tuptable.h"
 #include "fmgr.h"
 #include "nodes/params.h"
-#include "nodes/primnodes.h"
+#include "nodes/plannodes.h"
 #include "utils/tuplestore.h"
 
 
@@ -316,6 +315,8 @@ typedef struct EState
 	List	   *es_rowMark;		/* not good place, but there is no other */
 	MemoryContext es_query_cxt; /* per-query context in which EState lives */
 
+	bool		es_instrument;	/* true requests runtime instrumentation */
+
 	/*
 	 * this ExprContext is for per-output-tuple operations, such as
 	 * constraint checks and index-value computations.	It will be reset
@@ -332,98 +333,101 @@ typedef struct EState
 	bool		es_useEvalPlan;
 } EState;
 
-/* ----------------
- *		Executor Type information needed by plannodes.h
- *
- *|		Note: the bogus classes CommonState and CommonScanState exist only
- *|			  because our inheritance system only allows single inheritance
- *|			  and we have to have unique slot names.  Hence two or more
- *|			  classes which want to have a common slot must ALL inherit
- *|			  the slot from some other class.  (This is a big hack to
- *|			  allow our classes to share slot names..)
- *|
- *|		Example:
- *|			  the class Result and the class NestLoop nodes both want
- *|			  a slot called "OuterTuple" so they both have to inherit
- *|			  it from some other class.  In this case they inherit
- *|			  it from CommonState.	"CommonState" and "CommonScanState" are
- *|			  the best names I could come up with for this sort of
- *|			  stuff.
- *|
- *|			  As a result, many classes have extra slots which they
- *|			  don't use.  These slots are denoted (unused) in the
- *|			  comment preceding the class definition.	If you
- *|			  comes up with a better idea of a way of doing things
- *|			  along these lines, then feel free to make your idea
- *|			  known to me.. -cim 10/15/89
- * ----------------
- */
 
 /* ----------------------------------------------------------------
- *				 Common Executor State Information
+ *				 Executor State Information
  * ----------------------------------------------------------------
  */
 
 /* ----------------
- *	 CommonState information
- *
- *		Superclass for all executor node-state object types.
+ *		PlanState node
  *
- *		OuterTupleSlot	   pointer to slot containing current "outer" tuple
- *		ResultTupleSlot    pointer to slot in tuple table for projected tuple
- *		ExprContext		   node's expression-evaluation context
- *		ProjInfo		   info this node uses to form tuple projections
- *		TupFromTlist	   state flag used by some node types (why kept here?)
+ * We never actually instantiate any PlanState nodes; this is just the common
+ * abstract superclass for all PlanState-type nodes.
  * ----------------
  */
-typedef struct CommonState
+typedef struct PlanState
 {
-	NodeTag		type;			/* its first field is NodeTag */
-	TupleTableSlot *cs_OuterTupleSlot;
-	TupleTableSlot *cs_ResultTupleSlot;
-	ExprContext *cs_ExprContext;
-	ProjectionInfo *cs_ProjInfo;
-	bool		cs_TupFromTlist;
-} CommonState;
+	NodeTag		type;
 
+	Plan	   *plan;			/* associated Plan node */
 
-/* ----------------------------------------------------------------
- *				 Control Node State Information
- * ----------------------------------------------------------------
+	EState	   *state;			/* at execution time, state's of
+								 * individual nodes point to one EState
+								 * for the whole top-level plan */
+
+	struct Instrumentation *instrument; /* Optional runtime stats for this
+										 * plan node */
+
+	/*
+	 * Common structural data for all Plan types.  These links to subsidiary
+	 * state trees parallel links in the associated plan tree (except for
+	 * the subPlan list, which does not exist in the plan tree).
+	 */
+	List	   *targetlist;		/* target list to be computed at this node */
+	List	   *qual;			/* implicitly-ANDed qual conditions */
+	struct PlanState *lefttree;	/* input plan tree(s) */
+	struct PlanState *righttree;
+	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated
+								 * expr subselects) */
+	List	   *subPlan;		/* SubPlanState nodes in my expressions */
+
+	/*
+	 * State for management of parameter-change-driven rescanning
+	 */
+	List	   *chgParam;		/* integer list of IDs of changed Params */
+
+	/*
+	 * Other run-time state needed by most if not all node types.
+	 */
+	TupleTableSlot *ps_OuterTupleSlot; /* slot for current "outer" tuple */
+	TupleTableSlot *ps_ResultTupleSlot;	/* slot for my result tuples */
+	ExprContext *ps_ExprContext; /* node's expression-evaluation context */
+	ProjectionInfo *ps_ProjInfo; /* info for doing tuple projection */
+	bool		ps_TupFromTlist; /* state flag for processing set-valued
+								  * functions in targetlist */
+} PlanState;
+
+/* ----------------
+ *	these are are defined to avoid confusion problems with "left"
+ *	and "right" and "inner" and "outer".  The convention is that
+ *	the "left" plan is the "outer" plan and the "right" plan is
+ *	the inner plan, but these make the code more readable.
+ * ----------------
  */
+#define innerPlanState(node)		(((PlanState *)(node))->righttree)
+#define outerPlanState(node)		(((PlanState *)(node))->lefttree)
+
 
 /* ----------------
  *	 ResultState information
- *
- *		done			   flag which tells us to quit when we
- *						   have already returned a constant tuple.
  * ----------------
  */
 typedef struct ResultState
 {
-	CommonState cstate;			/* its first field is NodeTag */
-	bool		rs_done;
-	bool		rs_checkqual;
+	PlanState	ps;				/* its first field is NodeTag */
+	Node	   *resconstantqual;
+	bool		rs_done;		/* are we done? */
+	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
 
 /* ----------------
  *	 AppendState information
  *
+ *		nplans			how many plans are in the list
  *		whichplan		which plan is being executed (0 .. n-1)
  *		firstplan		first plan to execute (usually 0)
  *		lastplan		last plan to execute (usually n-1)
- *		nplans			how many plans are in the list
- *		initialized		array of ExecInitNode() results
  * ----------------
  */
 typedef struct AppendState
 {
-	CommonState cstate;			/* its first field is NodeTag */
+	PlanState	ps;				/* its first field is NodeTag */
+	PlanState **appendplans;	/* array of PlanStates for my inputs */
+	int			as_nplans;
 	int			as_whichplan;
 	int			as_firstplan;
 	int			as_lastplan;
-	int			as_nplans;
-	bool	   *as_initialized;
 } AppendState;
 
 /* ----------------------------------------------------------------
@@ -432,9 +436,9 @@ typedef struct AppendState
  */
 
 /* ----------------
- *	 CommonScanState information
+ *	 ScanState information
  *
- *		CommonScanState extends CommonState for node types that represent
+ *		ScanState extends PlanState for node types that represent
  *		scans of an underlying relation.  It can also be used for nodes
  *		that scan the output of an underlying plan node --- in that case,
  *		only ScanTupleSlot is actually useful, and it refers to the tuple
@@ -445,27 +449,23 @@ typedef struct AppendState
  *		ScanTupleSlot	   pointer to slot in tuple table holding scan tuple
  * ----------------
  */
-typedef struct CommonScanState
+typedef struct ScanState
 {
-	CommonState cstate;			/* its first field is NodeTag */
-	Relation	css_currentRelation;
-	HeapScanDesc css_currentScanDesc;
-	TupleTableSlot *css_ScanTupleSlot;
-} CommonScanState;
+	PlanState	ps;				/* its first field is NodeTag */
+	Relation	ss_currentRelation;
+	HeapScanDesc ss_currentScanDesc;
+	TupleTableSlot *ss_ScanTupleSlot;
+} ScanState;
 
 /*
- * SeqScan uses a bare CommonScanState as its state item, since it needs
+ * SeqScan uses a bare ScanState as its state node, since it needs
  * no additional fields.
  */
+typedef ScanState SeqScanState;
 
 /* ----------------
  *	 IndexScanState information
  *
- *		Note that an IndexScan node *also* has a CommonScanState state item.
- *		IndexScanState stores the info needed specifically for indexing.
- *		There's probably no good reason why this is a separate node type
- *		rather than an extension of CommonScanState.
- *
  *		NumIndices		   number of indices in this scan
  *		IndexPtr		   current index in use
  *		ScanKeys		   Skey structures to scan index rels
@@ -479,7 +479,9 @@ typedef struct CommonScanState
  */
 typedef struct IndexScanState
 {
-	NodeTag		type;
+	ScanState	ss;				/* its first field is NodeTag */
+	List	   *indxqual;
+	List	   *indxqualorig;
 	int			iss_NumIndices;
 	int			iss_IndexPtr;
 	int			iss_MarkIndexPtr;
@@ -495,10 +497,6 @@ typedef struct IndexScanState
 /* ----------------
  *	 TidScanState information
  *
- *		Note that a TidScan node *also* has a CommonScanState state item.
- *		There's probably no good reason why this is a separate node type
- *		rather than an extension of CommonScanState.
- *
  *		NumTids		   number of tids in this scan
  *		TidPtr		   current tid in use
  *		TidList		   evaluated item pointers
@@ -506,7 +504,7 @@ typedef struct IndexScanState
  */
 typedef struct TidScanState
 {
-	NodeTag		type;
+	ScanState	ss;				/* its first field is NodeTag */
 	int			tss_NumTids;
 	int			tss_TidPtr;
 	int			tss_MarkTidPtr;
@@ -526,7 +524,8 @@ typedef struct TidScanState
  */
 typedef struct SubqueryScanState
 {
-	CommonScanState csstate;	/* its first field is NodeTag */
+	ScanState	ss;				/* its first field is NodeTag */
+	PlanState  *subplan;
 	EState	   *sss_SubEState;
 } SubqueryScanState;
 
@@ -538,12 +537,12 @@ typedef struct SubqueryScanState
  *
  *		tupdesc				expected return tuple description
  *		tuplestorestate		private state of tuplestore.c
- *		funcexpr			function expression being evaluated
+ *		funcexpr			state for function expression being evaluated
  * ----------------
  */
 typedef struct FunctionScanState
 {
-	CommonScanState csstate;	/* its first field is NodeTag */
+	ScanState	ss;				/* its first field is NodeTag */
 	TupleDesc	tupdesc;
 	Tuplestorestate *tuplestorestate;
 	Node	   *funcexpr;
@@ -557,11 +556,15 @@ typedef struct FunctionScanState
 /* ----------------
  *	 JoinState information
  *
- *		Superclass for state items of join nodes.
- *		Currently this is the same as CommonState.
+ *		Superclass for state nodes of join plans.
  * ----------------
  */
-typedef CommonState JoinState;
+typedef struct JoinState
+{
+	PlanState	ps;
+	JoinType	jointype;
+	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+} JoinState;
 
 /* ----------------
  *	 NestLoopState information
@@ -573,7 +576,7 @@ typedef CommonState JoinState;
  */
 typedef struct NestLoopState
 {
-	JoinState	jstate;			/* its first field is NodeTag */
+	JoinState	js;				/* its first field is NodeTag */
 	bool		nl_NeedNewOuter;
 	bool		nl_MatchedOuter;
 	TupleTableSlot *nl_NullInnerTupleSlot;
@@ -596,7 +599,8 @@ typedef struct NestLoopState
  */
 typedef struct MergeJoinState
 {
-	JoinState	jstate;			/* its first field is NodeTag */
+	JoinState	js;				/* its first field is NodeTag */
+	List	   *mergeclauses;
 	List	   *mj_OuterSkipQual;
 	List	   *mj_InnerSkipQual;
 	int			mj_JoinState;
@@ -630,7 +634,8 @@ typedef struct MergeJoinState
  */
 typedef struct HashJoinState
 {
-	JoinState	jstate;			/* its first field is NodeTag */
+	JoinState	js;				/* its first field is NodeTag */
+	List	   *hashclauses;
 	HashJoinTable hj_HashTable;
 	int			hj_CurBucketNo;
 	HashJoinTuple hj_CurTuple;
@@ -656,23 +661,46 @@ typedef struct HashJoinState
  *		materialize nodes are used to materialize the results
  *		of a subplan into a temporary file.
  *
- *		csstate.css_ScanTupleSlot refers to output of underlying plan.
+ *		ss.ss_ScanTupleSlot refers to output of underlying plan.
  *
  *		tuplestorestate		private state of tuplestore.c
  * ----------------
  */
 typedef struct MaterialState
 {
-	CommonScanState csstate;	/* its first field is NodeTag */
+	ScanState	ss;				/* its first field is NodeTag */
 	void	   *tuplestorestate;
 } MaterialState;
 
+/* ----------------
+ *	 SortState information
+ * ----------------
+ */
+typedef struct SortState
+{
+	ScanState	ss;				/* its first field is NodeTag */
+	bool		sort_Done;		/* sort completed yet? */
+	void	   *tuplesortstate;	/* private state of tuplesort.c */
+} SortState;
+
 /* ---------------------
- *	AggregateState information
+ *	GroupState information
+ * -------------------------
+ */
+typedef struct GroupState
+{
+	ScanState	ss;				/* its first field is NodeTag */
+	FmgrInfo   *eqfunctions;	/* per-field lookup data for equality fns */
+	HeapTuple	grp_firstTuple;	/* copy of first tuple of current group */
+	bool		grp_done;		/* indicates completion of Group scan */
+} GroupState;
+
+/* ---------------------
+ *	AggState information
  *
- *	csstate.css_ScanTupleSlot refers to output of underlying plan.
+ *	ss.ss_ScanTupleSlot refers to output of underlying plan.
  *
- *	Note: csstate.cstate.cs_ExprContext contains ecxt_aggvalues and
+ *	Note: ss.ps.ps_ExprContext contains ecxt_aggvalues and
  *	ecxt_aggnulls arrays, which hold the computed agg values for the current
  *	input group during evaluation of an Agg node's output tuple(s).  We
  *	create a second ExprContext, tmpcontext, in which to evaluate input
@@ -687,7 +715,7 @@ typedef struct AggHashTableData *AggHashTable;
 
 typedef struct AggState
 {
-	CommonScanState csstate;	/* its first field is NodeTag */
+	ScanState	ss;				/* 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 */
@@ -705,32 +733,6 @@ typedef struct AggState
 	int			next_hash_bucket; /* next chain */
 } AggState;
 
-/* ---------------------
- *	GroupState information
- * -------------------------
- */
-typedef struct GroupState
-{
-	CommonScanState csstate;	/* its first field is NodeTag */
-	FmgrInfo   *eqfunctions;	/* per-field lookup data for equality fns */
-	HeapTuple	grp_firstTuple;	/* copy of first tuple of current group */
-	bool		grp_done;		/* indicates completion of Group scan */
-} GroupState;
-
-/* ----------------
- *	 SortState information
- *
- *		sort_Done		indicates whether sort has been performed yet
- *		tuplesortstate	private state of tuplesort.c
- * ----------------
- */
-typedef struct SortState
-{
-	CommonScanState csstate;	/* its first field is NodeTag */
-	bool		sort_Done;
-	void	   *tuplesortstate;
-} SortState;
-
 /* ----------------
  *	 UniqueState information
  *
@@ -744,12 +746,22 @@ typedef struct SortState
  */
 typedef struct UniqueState
 {
-	CommonState cstate;			/* its first field is NodeTag */
+	PlanState	ps;				/* its first field is NodeTag */
 	FmgrInfo   *eqfunctions;	/* per-field lookup data for equality fns */
 	HeapTuple	priorTuple;		/* most recently returned tuple, or NULL */
 	MemoryContext tempContext;	/* short-term context for comparisons */
 } UniqueState;
 
+/* ----------------
+ *	 HashState information
+ * ----------------
+ */
+typedef struct HashState
+{
+	PlanState	ps;				/* its first field is NodeTag */
+	HashJoinTable hashtable;	/* hash table for the hashjoin */
+} HashState;
+
 /* ----------------
  *	 SetOpState information
  *
@@ -761,7 +773,7 @@ typedef struct UniqueState
  */
 typedef struct SetOpState
 {
-	CommonState cstate;			/* its first field is NodeTag */
+	PlanState	ps;				/* its first field is NodeTag */
 	FmgrInfo   *eqfunctions;	/* per-field lookup data for equality fns */
 	bool		subplan_done;	/* has subplan returned EOF? */
 	long		numLeft;		/* number of left-input dups of cur group */
@@ -794,7 +806,9 @@ typedef enum
 
 typedef struct LimitState
 {
-	CommonState cstate;			/* its first field is NodeTag */
+	PlanState	ps;				/* its first field is NodeTag */
+	Node	   *limitOffset;	/* OFFSET parameter, or NULL if none */
+	Node	   *limitCount;		/* COUNT parameter, or NULL if none */
 	long		offset;			/* current OFFSET value */
 	long		count;			/* current COUNT, if any */
 	bool		noCount;		/* if true, ignore count */
@@ -803,46 +817,16 @@ typedef struct LimitState
 	TupleTableSlot *subSlot;	/* tuple last obtained from subplan */
 } LimitState;
 
-
-/* ----------------
- *	 HashState information
- *
- *		hashtable			hash table for the hashjoin
- * ----------------
+/* ---------------------
+ *	 SubPlanState information
+ * ---------------------
  */
-typedef struct HashState
+typedef struct SubPlanState
 {
-	CommonState cstate;			/* its first field is NodeTag */
-	HashJoinTable hashtable;
-} HashState;
-
-#ifdef NOT_USED
-/* -----------------------
- *	TeeState information
- *	  leftPlace  :	  next item in the queue unseen by the left parent
- *	  rightPlace :	  next item in the queue unseen by the right parent
- *	  lastPlace  :	  last item in the queue
- *	  bufferRelname :  name of the relation used as the buffer queue
- *	  bufferRel		:  the relation used as the buffer queue
- *	  mcxt			:  for now, tee's have their own memory context
- *					   may be cleaned up later if portals are cleaned up
- *
- * initially, a Tee starts with [left/right]Place variables set to	-1.
- * on cleanup, queue is free'd when both leftPlace and rightPlace = -1
- * -------------------------
-*/
-typedef struct TeeState
-{
-	CommonState cstate;			/* its first field is NodeTag */
-	int			tee_leftPlace,
-				tee_rightPlace,
-				tee_lastPlace;
-	char	   *tee_bufferRelname;
-	Relation	tee_bufferRel;
-	MemoryContext tee_mcxt;
-	HeapScanDesc tee_leftScanDesc,
-				tee_rightScanDesc;
-}	TeeState;
-#endif
+	PlanState	ps;				/* its first field is NodeTag */
+	PlanState  *planstate;		/* subselect plan's state tree */
+	bool		needShutdown;	/* TRUE = need to shutdown subplan */
+	HeapTuple	curTuple;		/* copy of most recent tuple from subplan */
+} SubPlanState;
 
 #endif   /* EXECNODES_H */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index a9cd095f94e..cb16a300442 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.125 2002/11/30 05:21:03 tgl Exp $
+ * $Id: nodes.h,v 1.126 2002/12/05 15:50:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,43 +18,84 @@
  * The first field of every node is NodeTag. Each node created (with makeNode)
  * will have one of the following tags as the value of its first field.
  *
- * Note that the number of the node tags are not contiguous. We left holes
+ * Note that the numbers of the node tags are not contiguous. We left holes
  * here so that we can add more tags without changing the existing enum's.
+ * (Since node tag numbers never exist outside backend memory, there's no
+ * real harm in renumbering, it just costs a full rebuild ...)
  */
 typedef enum NodeTag
 {
 	T_Invalid = 0,
 
+	/*
+	 * TAGS FOR EXECUTOR NODES (execnodes.h)
+	 */
+	T_IndexInfo = 10,
+	T_ResultRelInfo,
+	T_TupleTableSlot,
+	T_ExprContext,
+	T_ProjectionInfo,
+	T_JunkFilter,
+	T_EState,
+
 	/*
 	 * TAGS FOR PLAN NODES (plannodes.h)
 	 */
-	T_Plan = 10,
+	T_Plan = 100,
 	T_Result,
 	T_Append,
 	T_Scan,
 	T_SeqScan,
 	T_IndexScan,
+	T_TidScan,
+	T_SubqueryScan,
+	T_FunctionScan,
 	T_Join,
 	T_NestLoop,
 	T_MergeJoin,
 	T_HashJoin,
-	T_Limit,
 	T_Material,
 	T_Sort,
+	T_Group,
 	T_Agg,
 	T_Unique,
 	T_Hash,
 	T_SetOp,
-	T_Group,
+	T_Limit,
 	T_SubPlan,
-	T_TidScan,
-	T_SubqueryScan,
-	T_FunctionScan,
+
+	/*
+	 * TAGS FOR PLAN STATE NODES (execnodes.h)
+	 *
+	 * These should correspond one-to-one with Plan node types.
+	 */
+	T_PlanState = 200,
+	T_ResultState,
+	T_AppendState,
+	T_ScanState,
+	T_SeqScanState,
+	T_IndexScanState,
+	T_TidScanState,
+	T_SubqueryScanState,
+	T_FunctionScanState,
+	T_JoinState,
+	T_NestLoopState,
+	T_MergeJoinState,
+	T_HashJoinState,
+	T_MaterialState,
+	T_SortState,
+	T_GroupState,
+	T_AggState,
+	T_UniqueState,
+	T_HashState,
+	T_SetOpState,
+	T_LimitState,
+	T_SubPlanState,
 
 	/*
 	 * TAGS FOR PRIMITIVE NODES (primnodes.h)
 	 */
-	T_Resdom = 100,
+	T_Resdom = 300,
 	T_Fjoin,
 	T_Expr,
 	T_Var,
@@ -74,7 +115,7 @@ typedef enum NodeTag
 	/*
 	 * TAGS FOR PLANNER NODES (relation.h)
 	 */
-	T_RelOptInfo = 200,
+	T_RelOptInfo = 400,
 	T_IndexOptInfo,
 	T_Path,
 	T_IndexPath,
@@ -90,48 +131,16 @@ typedef enum NodeTag
 	T_JoinInfo,
 	T_InnerIndexscanInfo,
 
-	/*
-	 * TAGS FOR EXECUTOR NODES (execnodes.h)
-	 */
-	T_IndexInfo = 300,
-	T_ResultRelInfo,
-	T_TupleTableSlot,
-	T_ExprContext,
-	T_ProjectionInfo,
-	T_JunkFilter,
-	T_EState,
-	T_CommonState,
-	T_ResultState,
-	T_AppendState,
-	T_CommonScanState,
-	T_ScanState,
-	T_IndexScanState,
-	T_JoinState,
-	T_NestLoopState,
-	T_MergeJoinState,
-	T_HashJoinState,
-	T_MaterialState,
-	T_AggState,
-	T_GroupState,
-	T_SortState,
-	T_UniqueState,
-	T_HashState,
-	T_TidScanState,
-	T_SubqueryScanState,
-	T_SetOpState,
-	T_LimitState,
-	T_FunctionScanState,
-
 	/*
 	 * TAGS FOR MEMORY NODES (memnodes.h)
 	 */
-	T_MemoryContext = 400,
+	T_MemoryContext = 500,
 	T_AllocSetContext,
 
 	/*
 	 * TAGS FOR VALUE NODES (pg_list.h)
 	 */
-	T_Value = 500,
+	T_Value = 600,
 	T_List,
 	T_Integer,
 	T_Float,
@@ -142,7 +151,7 @@ typedef enum NodeTag
 	/*
 	 * TAGS FOR PARSE TREE NODES (parsenodes.h)
 	 */
-	T_Query = 600,
+	T_Query = 700,
 	T_InsertStmt,
 	T_DeleteStmt,
 	T_UpdateStmt,
@@ -208,7 +217,7 @@ typedef enum NodeTag
 	T_ExecuteStmt,
 	T_DeallocateStmt,
 
-	T_A_Expr = 700,
+	T_A_Expr = 800,
 	T_ColumnRef,
 	T_ParamRef,
 	T_A_Const,
@@ -248,7 +257,7 @@ typedef enum NodeTag
 	/*
 	 * TAGS FOR FUNCTION-CALL CONTEXT AND RESULTINFO NODES (see fmgr.h)
 	 */
-	T_TriggerData = 800,		/* in commands/trigger.h */
+	T_TriggerData = 900,		/* in commands/trigger.h */
 	T_ReturnSetInfo				/* in nodes/execnodes.h */
 
 } NodeTag;
diff --git a/src/include/nodes/params.h b/src/include/nodes/params.h
index ef0e0682be6..8f2eff5165e 100644
--- a/src/include/nodes/params.h
+++ b/src/include/nodes/params.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: params.h,v 1.18 2002/11/25 21:29:42 tgl Exp $
+ * $Id: params.h,v 1.19 2002/12/05 15:50:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -88,7 +88,7 @@ typedef ParamListInfoData *ParamListInfo;
  *	  array of ParamExecData records, which is referenced through
  *	  es_param_exec_vals or ecxt_param_exec_vals.
  *
- *	  If execPlan is not NULL, it points to a SubPlan node that needs to
+ *	  If execPlan is not NULL, it points to a SubPlanState node that needs to
  *	  be executed to produce the value.  (This is done so that we can have
  *	  lazy evaluation of InitPlans: they aren't executed until/unless a
  *	  result value is needed.)  Otherwise the value is assumed to be valid
@@ -98,7 +98,7 @@ typedef ParamListInfoData *ParamListInfo;
 
 typedef struct ParamExecData
 {
-	void	   *execPlan;		/* should be "SubPlan *" */
+	void	   *execPlan;		/* should be "SubPlanState *" */
 	Datum		value;
 	bool		isnull;
 } ParamExecData;
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 6a6ac415f9f..097f8d93b13 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -7,52 +7,15 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: plannodes.h,v 1.61 2002/11/30 00:08:22 tgl Exp $
+ * $Id: plannodes.h,v 1.62 2002/12/05 15:50:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #ifndef PLANNODES_H
 #define PLANNODES_H
 
-#include "nodes/execnodes.h"
-
-/* ----------------------------------------------------------------
- *	Executor State types are used in the plannode structures
- *	so we have to include their definitions too.
- *
- *		Node Type				node information used by executor
- *
- * control nodes
- *
- *		Result					ResultState				resstate;
- *		Append					AppendState				appendstate;
- *
- * scan nodes
- *
- *		Scan ***				CommonScanState			scanstate;
- *		IndexScan				IndexScanState			indxstate;
- *		SubqueryScan			SubqueryScanState		subquerystate;
- *		FunctionScan			FunctionScanState		functionstate;
- *
- *		  (*** nodes which inherit Scan also inherit scanstate)
- *
- * join nodes
- *
- *		NestLoop				NestLoopState			nlstate;
- *		MergeJoin				MergeJoinState			mergestate;
- *		HashJoin				HashJoinState			hashjoinstate;
- *
- * materialize nodes
- *
- *		Material				MaterialState			matstate;
- *		Sort					SortState				sortstate;
- *		Unique					UniqueState				uniquestate;
- *		SetOp					SetOpState				setopstate;
- *		Limit					LimitState				limitstate;
- *		Hash					HashState				hashstate;
- *
- * ----------------------------------------------------------------
- */
+#include "access/sdir.h"
+#include "nodes/primnodes.h"
 
 
 /* ----------------------------------------------------------------
@@ -62,45 +25,47 @@
 
 /* ----------------
  *		Plan node
+ *
+ * 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)
+ *
+ * We never actually instantiate any Plan nodes; this is just the common
+ * abstract superclass for all Plan-type nodes.
  * ----------------
  */
-
 typedef struct Plan
 {
 	NodeTag		type;
 
-	/* estimated execution costs for plan (see costsize.c for more info) */
+	/*
+	 * estimated execution costs for plan (see costsize.c for more info)
+	 */
 	Cost		startup_cost;	/* cost expended before fetching any
 								 * tuples */
 	Cost		total_cost;		/* total cost (assuming all tuples
 								 * fetched) */
 
 	/*
-	 * planner's estimate of result size (note: LIMIT, if any, is not
-	 * considered in setting plan_rows)
+	 * planner's estimate of result size of this plan step
 	 */
 	double		plan_rows;		/* number of rows plan is expected to emit */
 	int			plan_width;		/* average row width in bytes */
 
 	/*
-	 * execution state data.  Having Plan point to this, rather than the
-	 * other way round, is 100% bogus.
+	 * Common structural data for all Plan types.
 	 */
-	EState	   *state;			/* at execution time, state's of
-								 * individual nodes point to one EState
-								 * for the whole top-level plan */
-
-	struct Instrumentation *instrument; /* Optional runtime stats for this
-										 * plan node */
+	List	   *targetlist;		/* target list to be computed at this node */
+	List	   *qual;			/* implicitly-ANDed qual conditions */
+	struct Plan *lefttree;		/* input plan tree(s) */
+	struct Plan *righttree;
+	List	   *initPlan;		/* Init Plan nodes (un-correlated expr
+								 * subselects) */
 
 	/*
-	 * Common structural data for all Plan types.  XXX chgParam is runtime
-	 * data and should be in the EState, not here.
+	 * Information for management of parameter-change-driven rescanning
 	 */
-	List	   *targetlist;
-	List	   *qual;			/* implicitly-ANDed qual conditions */
-	struct Plan *lefttree;
-	struct Plan *righttree;
 	List	   *extParam;		/* indices of _all_ _external_ PARAM_EXEC
 								 * for this plan in global
 								 * es_param_exec_vals. Params from
@@ -108,10 +73,6 @@ typedef struct Plan
 								 * included, but their execParam-s are
 								 * here!!! */
 	List	   *locParam;		/* someones from setParam-s */
-	List	   *chgParam;		/* list of changed ones from the above */
-	List	   *initPlan;		/* Init Plan nodes (un-correlated expr
-								 * subselects) */
-	List	   *subPlan;		/* Other SubPlan nodes */
 
 	/*
 	 * We really need in some TopPlan node to store range table and
@@ -134,20 +95,6 @@ typedef struct Plan
 #define outerPlan(node)			(((Plan *)(node))->lefttree)
 
 
-/*
- * ===============
- * Top-level nodes
- * ===============
- */
-
-/*
- * 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.
@@ -163,7 +110,6 @@ typedef struct Result
 {
 	Plan		plan;
 	Node	   *resconstantqual;
-	ResultState *resstate;
 } Result;
 
 /* ----------------
@@ -182,7 +128,6 @@ typedef struct Append
 	Plan		plan;
 	List	   *appendplans;
 	bool		isTarget;
-	AppendState *appendstate;
 } Append;
 
 /*
@@ -194,7 +139,6 @@ typedef struct Scan
 {
 	Plan		plan;
 	Index		scanrelid;		/* relid is index into the range table */
-	CommonScanState *scanstate;
 } Scan;
 
 /* ----------------
@@ -214,7 +158,6 @@ typedef struct IndexScan
 	List	   *indxqual;
 	List	   *indxqualorig;
 	ScanDirection indxorderdir;
-	IndexScanState *indxstate;
 } IndexScan;
 
 /* ----------------
@@ -224,9 +167,7 @@ typedef struct IndexScan
 typedef struct TidScan
 {
 	Scan		scan;
-	bool		needRescan;
 	List	   *tideval;
-	TidScanState *tidstate;
 } TidScan;
 
 /* ----------------
@@ -257,7 +198,6 @@ typedef struct FunctionScan
 {
 	Scan		scan;
 	/* no other fields needed at present */
-	/* scan.scanstate actually points at a FunctionScanState node */
 } FunctionScan;
 
 /*
@@ -296,7 +236,6 @@ typedef struct Join
 typedef struct NestLoop
 {
 	Join		join;
-	NestLoopState *nlstate;
 } NestLoop;
 
 /* ----------------
@@ -307,7 +246,6 @@ typedef struct MergeJoin
 {
 	Join		join;
 	List	   *mergeclauses;
-	MergeJoinState *mergestate;
 } MergeJoin;
 
 /* ----------------
@@ -318,9 +256,40 @@ typedef struct HashJoin
 {
 	Join		join;
 	List	   *hashclauses;
-	HashJoinState *hashjoinstate;
 } HashJoin;
 
+/* ----------------
+ *		materialization node
+ * ----------------
+ */
+typedef struct Material
+{
+	Plan		plan;
+} Material;
+
+/* ----------------
+ *		sort node
+ * ----------------
+ */
+typedef struct Sort
+{
+	Plan		plan;
+	int			keycount;
+} Sort;
+
+/* ---------------
+ *	 group node -
+ *		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;
+	int			numCols;		/* number of grouping columns */
+	AttrNumber *grpColIdx;		/* their indexes in the target list */
+} Group;
+
 /* ---------------
  *		aggregate node
  *
@@ -349,44 +318,8 @@ typedef struct Agg
 	int			numCols;		/* number of grouping columns */
 	AttrNumber *grpColIdx;		/* their indexes in the target list */
 	long		numGroups;		/* estimated number of groups in input */
-	AggState   *aggstate;
 } Agg;
 
-/* ---------------
- *	 group node -
- *		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;
-	int			numCols;		/* number of grouping columns */
-	AttrNumber *grpColIdx;		/* their indexes in the target list */
-	GroupState *grpstate;
-} Group;
-
-/* ----------------
- *		materialization node
- * ----------------
- */
-typedef struct Material
-{
-	Plan		plan;
-	MaterialState *matstate;
-} Material;
-
-/* ----------------
- *		sort node
- * ----------------
- */
-typedef struct Sort
-{
-	Plan		plan;
-	int			keycount;
-	SortState  *sortstate;
-} Sort;
-
 /* ----------------
  *		unique node
  * ----------------
@@ -397,9 +330,18 @@ typedef struct Unique
 	int			numCols;		/* number of columns to check for
 								 * uniqueness */
 	AttrNumber *uniqColIdx;		/* indexes into the target list */
-	UniqueState *uniquestate;
 } Unique;
 
+/* ----------------
+ *		hash build node
+ * ----------------
+ */
+typedef struct Hash
+{
+	Plan		plan;
+	List	   *hashkeys;
+} Hash;
+
 /* ----------------
  *		setop node
  * ----------------
@@ -420,7 +362,6 @@ typedef struct SetOp
 								 * duplicate-ness */
 	AttrNumber *dupColIdx;		/* indexes into the target list */
 	AttrNumber	flagColIdx;
-	SetOpState *setopstate;
 } SetOp;
 
 /* ----------------
@@ -432,44 +373,13 @@ typedef struct Limit
 	Plan		plan;
 	Node	   *limitOffset;	/* OFFSET parameter, or NULL if none */
 	Node	   *limitCount;		/* COUNT parameter, or NULL if none */
-	LimitState *limitstate;
 } Limit;
 
-/* ----------------
- *		hash build node
- * ----------------
- */
-typedef struct Hash
-{
-	Plan		plan;
-	List	   *hashkeys;
-	HashState  *hashstate;
-} Hash;
-
-#ifdef NOT_USED
-/* -------------------
- *		Tee node information
- *
- *	  leftParent :				the left parent of this node
- *	  rightParent:				the right parent of this node
- * -------------------
-*/
-typedef struct Tee
-{
-	Plan		plan;
-	Plan	   *leftParent;
-	Plan	   *rightParent;
-	TeeState   *teestate;
-	char	   *teeTableName;	/* the name of the table to materialize
-								 * the tee into */
-	List	   *rtentries;		/* the range table for the plan below the
-								 * Tee may be different than the parent
-								 * plans */
-}	Tee;
-#endif
-
 /* ---------------------
  *		SubPlan node
+ *
+ * XXX Perhaps does not belong in this file?  It's not really a Plan node.
+ * Should we make it inherit from Plan anyway?
  * ---------------------
  */
 typedef struct SubPlan
@@ -489,12 +399,7 @@ typedef struct SubPlan
 								 * about what to do with subselect's
 								 * results */
 
-	/*
-	 * Remaining fields are working state for executor; not used in
-	 * planning
-	 */
-	bool		needShutdown;	/* TRUE = need to shutdown subplan */
-	HeapTuple	curTuple;		/* copy of most recent tuple from subplan */
+	struct SubPlanState *pstate; /* XXX TEMPORARY HACK */
 } SubPlan;
 
 #endif   /* PLANNODES_H */
diff --git a/src/include/nodes/print.h b/src/include/nodes/print.h
index 7ebcef4f10f..07accf78f37 100644
--- a/src/include/nodes/print.h
+++ b/src/include/nodes/print.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: print.h,v 1.19 2002/09/04 20:31:44 momjian Exp $
+ * $Id: print.h,v 1.20 2002/12/05 15:50:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,10 +15,10 @@
 #define PRINT_H
 
 #include "nodes/parsenodes.h"
-#include "nodes/plannodes.h"
+#include "nodes/execnodes.h"
 
 
-#define nodeDisplay		pprint
+#define nodeDisplay(x)		pprint(x)
 
 extern void print(void *obj);
 extern void pprint(void *obj);
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 5f6ad686dff..333ca0b695b 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.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: relation.h,v 1.72 2002/11/30 05:21:03 tgl Exp $
+ * $Id: relation.h,v 1.73 2002/12/05 15:50:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -377,8 +377,7 @@ typedef struct IndexPath
 typedef struct TidPath
 {
 	Path		path;
-	List	   *tideval;			/* qual(s) involving CTID = something */
-	Relids		unjoined_relids;	/* some rels not yet part of my Path */
+	List	   *tideval;		/* qual(s) involving CTID = something */
 } TidPath;
 
 /*
diff --git a/src/include/tcop/pquery.h b/src/include/tcop/pquery.h
index a29b1857ec6..b0014249977 100644
--- a/src/include/tcop/pquery.h
+++ b/src/include/tcop/pquery.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: pquery.h,v 1.22 2002/09/04 20:31:45 momjian Exp $
+ * $Id: pquery.h,v 1.23 2002/12/05 15:50:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -21,8 +21,6 @@
 extern void ProcessQuery(Query *parsetree, Plan *plan, CommandDest dest,
 			 char *completionTag);
 
-extern EState *CreateExecutorState(void);
-
 extern Portal PreparePortal(char *portalName);
 
 #endif   /* PQUERY_H */
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index ba784fee03a..40258acc32a 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -3,19 +3,16 @@
  * portal.h
  *	  POSTGRES portal definitions.
  *
+ * A portal is an abstraction which represents the execution state of
+ * a running query (specifically, a CURSOR).
  *
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: portal.h,v 1.35 2002/09/04 20:31:45 momjian Exp $
+ * $Id: portal.h,v 1.36 2002/12/05 15:50:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
-/*
- * Note:
- *		A portal is an abstraction which represents the execution state of
- * a running query (specifically, a CURSOR).
- */
 #ifndef PORTAL_H
 #define PORTAL_H
 
@@ -30,8 +27,6 @@ typedef struct PortalData
 	char	   *name;			/* Portal's name */
 	MemoryContext heap;			/* subsidiary memory */
 	QueryDesc  *queryDesc;		/* Info about query associated with portal */
-	TupleDesc	attinfo;
-	EState	   *state;			/* Execution state of query */
 	bool		atStart;		/* T => fetch backwards is not allowed */
 	bool		atEnd;			/* T => fetch forwards is not allowed */
 	void		(*cleanup) (Portal);	/* Cleanup routine (optional) */
@@ -47,8 +42,6 @@ typedef struct PortalData
  * Access macros for Portal ... use these in preference to field access.
  */
 #define PortalGetQueryDesc(portal)	((portal)->queryDesc)
-#define PortalGetTupleDesc(portal)	((portal)->attinfo)
-#define PortalGetState(portal)		((portal)->state)
 #define PortalGetHeapMemory(portal) ((portal)->heap)
 
 /*
@@ -64,7 +57,6 @@ extern Portal CreatePortal(char *name);
 extern void PortalDrop(Portal portal);
 extern Portal GetPortalByName(char *name);
 extern void PortalSetQuery(Portal portal, QueryDesc *queryDesc,
-			   TupleDesc attinfo, EState *state,
-			   void (*cleanup) (Portal portal));
+						   void (*cleanup) (Portal portal));
 
 #endif   /* PORTAL_H */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 2bdf24116c9..007a3ffbe79 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.71 2002/11/30 21:25:08 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.72 2002/12/05 15:50:39 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -3583,7 +3583,6 @@ exec_simple_check_plan(PLpgSQL_expr * expr)
 	if (plan->lefttree != NULL ||
 		plan->righttree != NULL ||
 		plan->initPlan != NULL ||
-		plan->subPlan != NULL ||
 		plan->qual != NULL ||
 		((Result *) plan)->resconstantqual != NULL)
 		return;
-- 
GitLab