From 3a4f7dde16ad81b2319b9a4924a6023710a2fefd Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 13 Dec 2002 19:46:01 +0000
Subject: [PATCH] Phase 3 of read-only-plans project: ExecInitExpr now builds
 expression execution state trees, and ExecEvalExpr takes an expression state
 tree not an expression plan tree.  The plan tree is now read-only as far as
 the executor is concerned.  Next step is to begin actually exploiting this
 property.

---
 contrib/intagg/int_aggregate.c          |   1 -
 src/backend/bootstrap/bootstrap.c       |   5 +-
 src/backend/catalog/index.c             |  10 +-
 src/backend/commands/copy.c             |  26 +-
 src/backend/commands/explain.c          |  20 +-
 src/backend/commands/indexcmds.c        |   5 +-
 src/backend/commands/prepare.c          |  12 +-
 src/backend/commands/tablecmds.c        |   9 +-
 src/backend/commands/typecmds.c         |  14 +-
 src/backend/executor/execAmi.c          |  10 +-
 src/backend/executor/execMain.c         |   5 +-
 src/backend/executor/execProcnode.c     |  25 +-
 src/backend/executor/execQual.c         | 658 ++++++++++++++----------
 src/backend/executor/execTuples.c       |   3 +-
 src/backend/executor/execUtils.c        |  10 +-
 src/backend/executor/nodeAgg.c          |  22 +-
 src/backend/executor/nodeFunctionscan.c |   9 +-
 src/backend/executor/nodeGroup.c        |   6 +-
 src/backend/executor/nodeHash.c         |  11 +-
 src/backend/executor/nodeHashjoin.c     |  31 +-
 src/backend/executor/nodeIndexscan.c    | 128 ++---
 src/backend/executor/nodeLimit.c        |   6 +-
 src/backend/executor/nodeMergejoin.c    |  44 +-
 src/backend/executor/nodeNestloop.c     |   8 +-
 src/backend/executor/nodeResult.c       |   8 +-
 src/backend/executor/nodeSeqscan.c      |   6 +-
 src/backend/executor/nodeSubplan.c      |  95 ++--
 src/backend/executor/nodeSubqueryscan.c |   6 +-
 src/backend/executor/nodeTidscan.c      |  11 +-
 src/backend/nodes/copyfuncs.c           |  12 +-
 src/backend/nodes/nodeFuncs.c           |   3 +-
 src/backend/nodes/outfuncs.c            |   4 +-
 src/backend/nodes/readfuncs.c           |  11 +-
 src/backend/optimizer/path/indxpath.c   |   8 +-
 src/backend/optimizer/util/clauses.c    |  21 +-
 src/backend/parser/analyze.c            |   5 +-
 src/backend/utils/adt/sets.c            |  38 +-
 src/backend/utils/cache/Makefile        |   5 +-
 src/backend/utils/cache/fcache.c        |  53 --
 src/include/executor/executor.h         |  19 +-
 src/include/executor/nodeSubplan.h      |  15 +-
 src/include/executor/spi.h              |   3 +-
 src/include/nodes/execnodes.h           | 225 ++++++--
 src/include/nodes/nodes.h               |  30 +-
 src/include/nodes/params.h              |   8 +-
 src/include/nodes/primnodes.h           |  14 +-
 src/include/utils/fcache.h              |  70 ---
 src/pl/plpgsql/src/pl_exec.c            |  16 +-
 src/pl/plpgsql/src/plpgsql.h            |   4 +-
 49 files changed, 973 insertions(+), 795 deletions(-)
 delete mode 100644 src/backend/utils/cache/fcache.c
 delete mode 100644 src/include/utils/fcache.h

diff --git a/contrib/intagg/int_aggregate.c b/contrib/intagg/int_aggregate.c
index b9c39d400b5..fc08137d4d4 100644
--- a/contrib/intagg/int_aggregate.c
+++ b/contrib/intagg/int_aggregate.c
@@ -25,7 +25,6 @@
 #include "catalog/pg_proc.h"
 #include "catalog/pg_type.h"
 #include "executor/executor.h"
-#include "utils/fcache.h"
 #include "utils/sets.h"
 #include "utils/syscache.h"
 #include "access/tupmacs.h"
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index b1eb7eb21a8..a2180b3466f 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.145 2002/11/14 23:53:27 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.146 2002/12/13 19:45:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,6 +31,7 @@
 #include "catalog/catname.h"
 #include "catalog/index.h"
 #include "catalog/pg_type.h"
+#include "executor/executor.h"
 #include "libpq/pqsignal.h"
 #include "miscadmin.h"
 #include "storage/ipc.h"
@@ -1142,6 +1143,8 @@ index_register(Oid heap,
 	/* predicate will likely be null, but may as well copy it */
 	newind->il_info->ii_Predicate = (List *)
 		copyObject(indexInfo->ii_Predicate);
+	newind->il_info->ii_PredicateState = (List *)
+		ExecInitExpr((Expr *) newind->il_info->ii_Predicate, NULL);
 
 	newind->il_next = ILHead;
 	ILHead = newind;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index c11bd5b172b..4e72fc53f02 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.206 2002/12/12 15:49:24 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.207 2002/12/13 19:45:47 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -912,6 +912,7 @@ BuildIndexInfo(Form_pg_index indexStruct)
 
 	/*
 	 * If partial index, convert predicate into expression nodetree
+	 * and prepare an execution state nodetree for it
 	 */
 	if (VARSIZE(&indexStruct->indpred) > VARHDRSZ)
 	{
@@ -921,10 +922,15 @@ BuildIndexInfo(Form_pg_index indexStruct)
 								PointerGetDatum(&indexStruct->indpred)));
 		ii->ii_Predicate = stringToNode(predString);
 		fix_opfuncids((Node *) ii->ii_Predicate);
+		ii->ii_PredicateState = (List *)
+			ExecInitExpr((Expr *) ii->ii_Predicate, NULL);
 		pfree(predString);
 	}
 	else
+	{
 		ii->ii_Predicate = NIL;
+		ii->ii_PredicateState = NIL;
+	}
 
 	/* Other info */
 	ii->ii_Unique = indexStruct->indisunique;
@@ -1483,7 +1489,7 @@ IndexBuildHeapScan(Relation heapRelation,
 	Datum		attdata[INDEX_MAX_KEYS];
 	char		nulls[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate = indexInfo->ii_Predicate;
+	List	   *predicate = indexInfo->ii_PredicateState;
 	TupleTable	tupleTable;
 	TupleTableSlot *slot;
 	ExprContext *econtext;
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 41586331af0..9e8f7a46be4 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.185 2002/12/12 15:49:24 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.186 2002/12/13 19:45:48 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -758,7 +758,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 				num_defaults;
 	FmgrInfo   *in_functions;
 	Oid		   *elements;
-	Node	  **constraintexprs;
+	ExprState **constraintexprs;
 	bool		hasConstraints = false;
 	int			i;
 	List	   *cur;
@@ -772,7 +772,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 	TupleTableSlot *slot;
 	bool		file_has_oids;
 	int		   *defmap;
-	Node	  **defexprs;		/* array of default att expressions */
+	ExprState **defexprs;		/* array of default att expressions */
 	ExprContext *econtext;		/* used for ExecEvalExpr for default atts */
 	MemoryContext oldcontext = CurrentMemoryContext;
 
@@ -812,8 +812,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 	in_functions = (FmgrInfo *) palloc(num_phys_attrs * sizeof(FmgrInfo));
 	elements = (Oid *) palloc(num_phys_attrs * sizeof(Oid));
 	defmap = (int *) palloc(num_phys_attrs * sizeof(int));
-	defexprs = (Node **) palloc(num_phys_attrs * sizeof(Node *));
-	constraintexprs = (Node **) palloc0(num_phys_attrs * sizeof(Node *));
+	defexprs = (ExprState **) palloc(num_phys_attrs * sizeof(ExprState *));
+	constraintexprs = (ExprState **) palloc0(num_phys_attrs * sizeof(ExprState *));
 
 	for (i = 0; i < num_phys_attrs; i++)
 	{
@@ -837,10 +837,12 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 		{
 			/* attribute is NOT to be copied */
 			/* use default value if one exists */
-			defexprs[num_defaults] = build_column_default(rel, i + 1);
-			if (defexprs[num_defaults] != NULL)
+			Node   *defexpr = build_column_default(rel, i + 1);
+
+			if (defexpr != NULL)
 			{
-				fix_opfuncids(defexprs[num_defaults]);
+				fix_opfuncids(defexpr);
+				defexprs[num_defaults] = ExecInitExpr((Expr *) defexpr, NULL);
 				defmap[num_defaults] = i;
 				num_defaults++;
 			}
@@ -872,7 +874,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 			if (node != (Node *) prm)
 			{
 				fix_opfuncids(node);
-				constraintexprs[i] = node;
+				constraintexprs[i] = ExecInitExpr((Expr *) node, NULL);
 				hasConstraints = true;
 			}
 		}
@@ -1165,10 +1167,10 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 
 			for (i = 0; i < num_phys_attrs; i++)
 			{
-				Node	   *node = constraintexprs[i];
+				ExprState  *exprstate = constraintexprs[i];
 				bool		isnull;
 
-				if (node == NULL)
+				if (exprstate == NULL)
 					continue;	/* no constraint for this attr */
 
 				/* Insert current row's value into the Param value */
@@ -1180,7 +1182,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 				 * to replace the value (consider e.g. a timestamp precision
 				 * restriction).
 				 */
-				values[i] = ExecEvalExpr(node, econtext,
+				values[i] = ExecEvalExpr(exprstate, econtext,
 										 &isnull, NULL);
 				nulls[i] = isnull ? 'n' : ' ';
 			}
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index c587765bd5a..1f9e7543d6f 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.96 2002/12/12 15:49:24 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.97 2002/12/13 19:45:49 tgl Exp $
  *
  */
 
@@ -582,26 +582,24 @@ explain_outNode(StringInfo str,
 	if (plan->initPlan)
 	{
 		List	   *saved_rtable = es->rtable;
-		List	   *pslist = planstate->initPlan;
 		List	   *lst;
 
 		for (i = 0; i < indent; i++)
 			appendStringInfo(str, "  ");
 		appendStringInfo(str, "  InitPlan\n");
-		foreach(lst, plan->initPlan)
+		foreach(lst, planstate->initPlan)
 		{
-			SubPlanExpr  *subplan = (SubPlanExpr *) lfirst(lst);
-			SubPlanState *subplanstate = (SubPlanState *) lfirst(pslist);
+			SubPlanExprState *sps = (SubPlanExprState *) lfirst(lst);
+			SubPlanExpr *sp = (SubPlanExpr *) sps->xprstate.expr;
 
-			es->rtable = subplan->rtable;
+			es->rtable = sp->rtable;
 			for (i = 0; i < indent; i++)
 				appendStringInfo(str, "  ");
 			appendStringInfo(str, "    ->  ");
-			explain_outNode(str, subplan->plan,
-							subplanstate->planstate,
+			explain_outNode(str, sp->plan,
+							sps->planstate,
 							NULL,
 							indent + 4, es);
-			pslist = lnext(pslist);
 		}
 		es->rtable = saved_rtable;
 	}
@@ -689,8 +687,8 @@ explain_outNode(StringInfo str,
 		appendStringInfo(str, "  SubPlan\n");
 		foreach(lst, planstate->subPlan)
 		{
-			SubPlanState *sps = (SubPlanState *) lfirst(lst);
-			SubPlanExpr *sp = (SubPlanExpr *) sps->ps.plan;
+			SubPlanExprState *sps = (SubPlanExprState *) lfirst(lst);
+			SubPlanExpr *sp = (SubPlanExpr *) sps->xprstate.expr;
 
 			es->rtable = sp->rtable;
 			for (i = 0; i < indent; i++)
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d6ccdb92613..7b66eea0b0b 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.93 2002/12/12 15:49:24 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.94 2002/12/13 19:45:50 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,6 +24,7 @@
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_proc.h"
 #include "commands/defrem.h"
+#include "executor/executor.h"
 #include "miscadmin.h"
 #include "optimizer/clauses.h"
 #include "optimizer/planmain.h"
@@ -172,6 +173,8 @@ DefineIndex(RangeVar *heapRelation,
 	 */
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Predicate = cnfPred;
+	indexInfo->ii_PredicateState = (List *)
+		ExecInitExpr((Expr *) cnfPred, NULL);
 	indexInfo->ii_FuncOid = InvalidOid;
 	indexInfo->ii_Unique = unique;
 
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 9a570c8f681..98894372fa7 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.9 2002/12/05 15:50:30 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.10 2002/12/13 19:45:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -15,6 +15,7 @@
 #include "commands/prepare.h"
 #include "executor/executor.h"
 #include "utils/guc.h"
+#include "optimizer/planmain.h"
 #include "optimizer/planner.h"
 #include "rewrite/rewriteHandler.h"
 #include "tcop/pquery.h"
@@ -110,17 +111,22 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest)
 	{
 		int			nargs = length(entry->argtype_list);
 		int			i = 0;
+		List	   *exprstates;
 		ExprContext *econtext = MakeExprContext(NULL, CurrentMemoryContext);
 
 		/* Parser should have caught this error, but check */
 		if (nargs != length(stmt->params))
 			elog(ERROR, "ExecuteQuery: wrong number of arguments");
 
+		fix_opfuncids((Node *) stmt->params);
+
+		exprstates = (List *) ExecInitExpr((Expr *) stmt->params, NULL);
+
 		paramLI = (ParamListInfo) palloc0((nargs + 1) * sizeof(ParamListInfoData));
 
-		foreach(l, stmt->params)
+		foreach(l, exprstates)
 		{
-			Node	   *n = lfirst(l);
+			ExprState  *n = lfirst(l);
 			bool		isNull;
 
 			paramLI[i].value = ExecEvalExprSwitchContext(n,
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 9f5d89a87fb..a7a19c6f741 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.59 2002/12/12 20:35:12 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.60 2002/12/13 19:45:51 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2718,6 +2718,7 @@ AlterTableAddCheckConstraint(Relation rel, Constraint *constr)
 	HeapTuple	tuple;
 	RangeTblEntry *rte;
 	List	   *qual;
+	List	   *qualstate;
 	Node	   *expr;
 
 	/*
@@ -2769,6 +2770,9 @@ AlterTableAddCheckConstraint(Relation rel, Constraint *constr)
 
 	qual = makeList1(expr);
 
+	/* build execution state for qual */
+	qualstate = (List *) ExecInitExpr((Expr *) qual, NULL);
+
 	/* Make tuple slot to hold tuples */
 	slot = MakeTupleTableSlot();
 	ExecSetSlotDescriptor(slot, RelationGetDescr(rel), false);
@@ -2783,7 +2787,7 @@ AlterTableAddCheckConstraint(Relation rel, Constraint *constr)
 	while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecQual(qualstate, econtext, true))
 		{
 			successful = false;
 			break;
@@ -3820,6 +3824,7 @@ AlterTableCreateToastTable(Oid relOid, bool silent)
 	indexInfo->ii_KeyAttrNumbers[0] = 1;
 	indexInfo->ii_KeyAttrNumbers[1] = 2;
 	indexInfo->ii_Predicate = NIL;
+	indexInfo->ii_PredicateState = NIL;
 	indexInfo->ii_FuncOid = InvalidOid;
 	indexInfo->ii_Unique = true;
 
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 989bc36ee82..fc0030fe762 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.23 2002/12/12 20:35:12 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.24 2002/12/13 19:45:52 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -1244,7 +1244,8 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
 	Form_pg_type	typTup;
 	ExprContext *econtext;
 	char   *ccbin;
-	Node   *expr;
+	Expr   *expr;
+	ExprState *exprstate;
 	int		counter = 0;
 	Constraint *constr;
 
@@ -1336,10 +1337,11 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
 	 * Test all values stored in the attributes based on the domain
 	 * the constraint is being added to.
 	 */
-	expr = stringToNode(ccbin);
-	fix_opfuncids(expr);
+	expr = (Expr *) stringToNode(ccbin);
+	fix_opfuncids((Node *) expr);
+	exprstate = ExecInitExpr(expr, NULL);
 
-	/* Make an expression context for ExecQual */
+	/* Make an expression context for ExecEvalExpr */
 	econtext = MakeExprContext(NULL, CurrentMemoryContext);
 
 	rels = get_rels_with_domain(domainoid);
@@ -1375,7 +1377,7 @@ AlterDomainAddConstraint(List *names, Node *newConstraint)
 				econtext->domainValue_datum = d;
 				econtext->domainValue_isNull = isNull;
 
-				conResult = ExecEvalExpr(expr, econtext, &isNull, NULL);
+				conResult = ExecEvalExpr(exprstate, econtext, &isNull, NULL);
 
 				if (!isNull && !DatumGetBool(conResult))
 					elog(ERROR, "AlterDomainAddConstraint: Domain %s constraint %s failed",
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c
index 4ac4781ec7c..fa95ad6d992 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
  *
- *	$Header: /cvsroot/pgsql/src/backend/executor/execAmi.c,v 1.66 2002/12/05 15:50:30 tgl Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/executor/execAmi.c,v 1.67 2002/12/13 19:45:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -61,17 +61,19 @@ ExecReScan(PlanState *node, ExprContext *exprCtxt)
 
 		foreach(lst, node->initPlan)
 		{
-			PlanState  *splan = ((SubPlanState *) lfirst(lst))->planstate;
+			SubPlanExprState  *sstate = (SubPlanExprState *) lfirst(lst);
+			PlanState  *splan = sstate->planstate;
 
 			if (splan->plan->extParam != NIL)	/* don't care about child
 												 * locParam */
 				SetChangedParamList(splan, node->chgParam);
 			if (splan->chgParam != NIL)
-				ExecReScanSetParamPlan((SubPlanState *) lfirst(lst), node);
+				ExecReScanSetParamPlan(sstate, node);
 		}
 		foreach(lst, node->subPlan)
 		{
-			PlanState  *splan = ((SubPlanState *) lfirst(lst))->planstate;
+			SubPlanExprState  *sstate = (SubPlanExprState *) lfirst(lst);
+			PlanState  *splan = sstate->planstate;
 
 			if (splan->plan->extParam != NIL)
 				SetChangedParamList(splan, node->chgParam);
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index abd099c08c0..bac7398825e 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -26,7 +26,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.191 2002/12/12 15:49:24 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.192 2002/12/13 19:45:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1543,7 +1543,8 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 		{
 			qual = (List *) stringToNode(check[i].ccbin);
 			fix_opfuncids((Node *) qual);
-			resultRelInfo->ri_ConstraintExprs[i] = qual;
+			resultRelInfo->ri_ConstraintExprs[i] = (List *)
+				ExecInitExpr((Expr *) qual, NULL);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
diff --git a/src/backend/executor/execProcnode.c b/src/backend/executor/execProcnode.c
index 680a6da609b..59c798b267a 100644
--- a/src/backend/executor/execProcnode.c
+++ b/src/backend/executor/execProcnode.c
@@ -12,7 +12,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.32 2002/12/12 15:49:24 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execProcnode.c,v 1.33 2002/12/13 19:45:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -229,23 +229,29 @@ ExecInitNode(Plan *node, EState *estate)
 	foreach(subp, node->initPlan)
 	{
 		SubPlanExpr *subplan = (SubPlanExpr *) lfirst(subp);
+		SubPlanExprState *sstate;
 
 		Assert(IsA(subplan, SubPlanExpr));
-		subps = lappend(subps, ExecInitSubPlan(subplan, estate));
+		sstate = ExecInitExprInitPlan(subplan, result);
+		ExecInitSubPlan(sstate, estate);
+		subps = lappend(subps, sstate);
 	}
 	result->initPlan = subps;
 
 	/*
 	 * Initialize any subPlans present in this node.  These were found
-	 * by ExecInitExpr during initialization of the PlanState.
+	 * by ExecInitExpr during initialization of the PlanState.  Note we
+	 * must do this after initializing initPlans, in case their arguments
+	 * contain subPlans (is that actually possible? perhaps not).
 	 */
 	subps = NIL;
 	foreach(subp, result->subPlan)
 	{
-		SubPlanExpr *subplan = (SubPlanExpr *) lfirst(subp);
+		SubPlanExprState *sstate = (SubPlanExprState *) lfirst(subp);
 
-		Assert(IsA(subplan, SubPlanExpr));
-		subps = lappend(subps, ExecInitSubPlan(subplan, estate));
+		Assert(IsA(sstate, SubPlanExprState));
+		ExecInitSubPlan(sstate, estate);
+		subps = lappend(subps, sstate);
 	}
 	result->subPlan = subps;
 
@@ -492,14 +498,11 @@ ExecEndNode(PlanState *node)
 	if (node == NULL)
 		return;
 
-	if (node->instrument)
-		InstrEndLoop(node->instrument);
-
 	/* Clean up initPlans and subPlans */
 	foreach(subp, node->initPlan)
-		ExecEndSubPlan((SubPlanState *) lfirst(subp));
+		ExecEndSubPlan((SubPlanExprState *) lfirst(subp));
 	foreach(subp, node->subPlan)
-		ExecEndSubPlan((SubPlanState *) lfirst(subp));
+		ExecEndSubPlan((SubPlanExprState *) lfirst(subp));
 
 	if (node->chgParam != NIL)
 	{
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 79796f1c0b2..b529d045c4d 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.118 2002/12/12 20:35:12 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.119 2002/12/13 19:45:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -41,43 +41,52 @@
 #include "executor/nodeSubplan.h"
 #include "miscadmin.h"
 #include "parser/parse_expr.h"
+#include "utils/acl.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
-#include "utils/fcache.h"
 #include "utils/lsyscache.h"
 
 
 /* static function decls */
-static Datum ExecEvalAggref(Aggref *aggref, ExprContext *econtext,
-			   bool *isNull);
-static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext,
-				 bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalAggref(AggrefExprState *aggref,
+							ExprContext *econtext,
+							bool *isNull);
+static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
+							  ExprContext *econtext,
+							  bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
-static Datum ExecEvalOper(OpExpr *op, ExprContext *econtext,
+static Datum ExecEvalParam(Param *expression, ExprContext *econtext,
+						   bool *isNull);
+static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext,
 			 bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalDistinct(DistinctExpr *op, ExprContext *econtext,
-				 bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalFunc(FuncExpr *func, ExprContext *econtext,
+static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext,
 			 bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext,
+				 bool *isNull, ExprDoneCond *isDone);
 static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo,
 				 List *argList, ExprContext *econtext);
-static Datum ExecEvalNot(BoolExpr *notclause, ExprContext *econtext,
+static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
 						 bool *isNull);
-static Datum ExecEvalOr(BoolExpr *orExpr, ExprContext *econtext,
+static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
 						bool *isNull);
-static Datum ExecEvalAnd(BoolExpr *andExpr, ExprContext *econtext,
+static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
 						 bool *isNull);
-static Datum ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext,
+static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
 			 bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalNullTest(NullTest *ntest, ExprContext *econtext,
-				 bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalBooleanTest(BooleanTest *btest, ExprContext *econtext,
-					bool *isNull, ExprDoneCond *isDone);
-static Datum ExecEvalConstraintTest(ConstraintTest *constraint,
+static Datum ExecEvalNullTest(GenericExprState *nstate,
+							  ExprContext *econtext,
+							  bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalBooleanTest(GenericExprState *bstate,
+								 ExprContext *econtext,
+								 bool *isNull, ExprDoneCond *isDone);
+static Datum ExecEvalConstraintTest(ConstraintTestState *cstate,
 					   ExprContext *econtext,
 					   bool *isNull, ExprDoneCond *isDone);
 static Datum ExecEvalConstraintTestValue(ConstraintTestValue *conVal,
 					   ExprContext *econtext, bool *isNull);
+static Datum ExecEvalFieldSelect(GenericExprState *fstate,
+								 ExprContext *econtext,
+								 bool *isNull, ExprDoneCond *isDone);
 
 
 /*----------
@@ -106,11 +115,12 @@ static Datum ExecEvalConstraintTestValue(ConstraintTestValue *conVal,
  *----------
  */
 static Datum
-ExecEvalArrayRef(ArrayRef *arrayRef,
+ExecEvalArrayRef(ArrayRefExprState *astate,
 				 ExprContext *econtext,
 				 bool *isNull,
 				 ExprDoneCond *isDone)
 {
+	ArrayRef   *arrayRef = (ArrayRef *) astate->xprstate.expr;
 	ArrayType  *array_source;
 	ArrayType  *resultArray;
 	bool		isAssignment = (arrayRef->refassgnexpr != NULL);
@@ -124,7 +134,7 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
 	if (arrayRef->refexpr != NULL)
 	{
 		array_source = (ArrayType *)
-			DatumGetPointer(ExecEvalExpr((Node *) arrayRef->refexpr,
+			DatumGetPointer(ExecEvalExpr(astate->refexpr,
 										 econtext,
 										 isNull,
 										 isDone));
@@ -150,13 +160,13 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
 		array_source = NULL;
 	}
 
-	foreach(elt, arrayRef->refupperindexpr)
+	foreach(elt, astate->refupperindexpr)
 	{
 		if (i >= MAXDIM)
 			elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
 				 MAXDIM);
 
-		upper.indx[i++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt),
+		upper.indx[i++] = DatumGetInt32(ExecEvalExpr((ExprState *) lfirst(elt),
 													 econtext,
 													 isNull,
 													 NULL));
@@ -170,15 +180,15 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
 		}
 	}
 
-	if (arrayRef->reflowerindexpr != NIL)
+	if (astate->reflowerindexpr != NIL)
 	{
-		foreach(elt, arrayRef->reflowerindexpr)
+		foreach(elt, astate->reflowerindexpr)
 		{
 			if (j >= MAXDIM)
 				elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
 					 MAXDIM);
 
-			lower.indx[j++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt),
+			lower.indx[j++] = DatumGetInt32(ExecEvalExpr((ExprState *) lfirst(elt),
 														 econtext,
 														 isNull,
 														 NULL));
@@ -205,7 +215,7 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
 
 	if (isAssignment)
 	{
-		Datum		sourceData = ExecEvalExpr((Node *) arrayRef->refassgnexpr,
+		Datum		sourceData = ExecEvalExpr(astate->refassgnexpr,
 											  econtext,
 											  isNull,
 											  NULL);
@@ -275,7 +285,7 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalAggref(Aggref *aggref, ExprContext *econtext, bool *isNull)
+ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, bool *isNull)
 {
 	if (econtext->ecxt_aggvalues == NULL)		/* safety check */
 		elog(ERROR, "ExecEvalAggref: no aggregates in this expression context");
@@ -403,7 +413,7 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
  *		   (in which case we could return NULL)?	-cim 10/13/89
  * ----------------------------------------------------------------
  */
-Datum
+static Datum
 ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
 {
 	int			thisParamKind = expression->paramkind;
@@ -574,6 +584,30 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
 	return retval;
 }
 
+/*
+ * init_fcache - initialize a FuncExprState node during first use
+ */
+void
+init_fcache(Oid foid, FuncExprState *fcache, MemoryContext fcacheCxt)
+{
+	AclResult	aclresult;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, get_func_name(foid));
+
+	/* Safety check (should never fail, as parser should check sooner) */
+	if (length(fcache->args) > FUNC_MAX_ARGS)
+		elog(ERROR, "init_fcache: too many arguments");
+
+	/* Set up the primary fmgr lookup information */
+	fmgr_info_cxt(foid, &(fcache->func), fcacheCxt);
+
+	/* Initialize additional info */
+	fcache->setArgsValid = false;
+}
+
 /*
  * Evaluate arguments for a function.
  */
@@ -593,7 +627,7 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo,
 	{
 		ExprDoneCond thisArgIsDone;
 
-		fcinfo->arg[i] = ExecEvalExpr((Node *) lfirst(arg),
+		fcinfo->arg[i] = ExecEvalExpr((ExprState *) lfirst(arg),
 									  econtext,
 									  &fcinfo->argnull[i],
 									  &thisArgIsDone);
@@ -624,12 +658,12 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo,
  * Evaluate the arguments to a function and then the function itself.
  */
 Datum
-ExecMakeFunctionResult(FunctionCachePtr fcache,
-					   List *arguments,
+ExecMakeFunctionResult(FuncExprState *fcache,
 					   ExprContext *econtext,
 					   bool *isNull,
 					   ExprDoneCond *isDone)
 {
+	List	   *arguments = fcache->args;
 	Datum		result;
 	FunctionCallInfoData fcinfo;
 	ReturnSetInfo rsinfo;		/* for functions returning sets */
@@ -823,7 +857,7 @@ ExecMakeFunctionResult(FunctionCachePtr fcache,
  * object.	(If function returns an empty set, we just return NULL instead.)
  */
 Tuplestorestate *
-ExecMakeTableFunctionResult(Node *funcexpr,
+ExecMakeTableFunctionResult(ExprState *funcexpr,
 							ExprContext *econtext,
 							TupleDesc expectedDesc,
 							TupleDesc *returnDesc)
@@ -841,7 +875,7 @@ ExecMakeTableFunctionResult(Node *funcexpr,
 	bool		returnsTuple = false;
 
 	/*
-	 * Normally the passed expression tree will be a FuncExpr, since the
+	 * Normally the passed expression tree will be a FuncExprState, since the
 	 * grammar only allows a function call at the top level of a table
 	 * function reference.  However, if the function doesn't return set then
 	 * the planner might have replaced the function call via constant-folding
@@ -850,11 +884,10 @@ ExecMakeTableFunctionResult(Node *funcexpr,
 	 * we don't get a chance to pass a special ReturnSetInfo to any functions
 	 * buried in the expression.
 	 */
-	if (funcexpr && IsA(funcexpr, FuncExpr))
+	if (funcexpr && IsA(funcexpr, FuncExprState) &&
+		IsA(funcexpr->expr, FuncExpr))
 	{
-		FuncExpr   *func = (FuncExpr *) funcexpr;
-		List	   *argList;
-		FunctionCachePtr fcache;
+		FuncExprState *fcache = (FuncExprState *) funcexpr;
 		ExprDoneCond argDone;
 
 		/*
@@ -862,19 +895,14 @@ ExecMakeTableFunctionResult(Node *funcexpr,
 		 */
 		direct_function_call = true;
 
-		funcrettype = func->funcresulttype;
-		argList = func->args;
-
 		/*
-		 * get the fcache from the FuncExpr node. If it is NULL, then
-		 * initialize it
+		 * Initialize function cache if first time through
 		 */
-		fcache = func->func_fcache;
-		if (fcache == NULL)
+		if (fcache->func.fn_oid == InvalidOid)
 		{
-			fcache = init_fcache(func->funcid, length(argList),
-								 econtext->ecxt_per_query_memory);
-			func->func_fcache = fcache;
+			FuncExpr *func = (FuncExpr *) fcache->xprstate.expr;
+
+			init_fcache(func->funcid, fcache, econtext->ecxt_per_query_memory);
 		}
 
 		/*
@@ -887,7 +915,7 @@ ExecMakeTableFunctionResult(Node *funcexpr,
 		 */
 		MemSet(&fcinfo, 0, sizeof(fcinfo));
 		fcinfo.flinfo = &(fcache->func);
-		argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext);
+		argDone = ExecEvalFuncArgs(&fcinfo, fcache->args, econtext);
 		/* We don't allow sets in the arguments of the table function */
 		if (argDone != ExprSingleResult)
 			elog(ERROR, "Set-valued function called in context that cannot accept a set");
@@ -914,9 +942,10 @@ ExecMakeTableFunctionResult(Node *funcexpr,
 	{
 		/* Treat funcexpr as a generic expression */
 		direct_function_call = false;
-		funcrettype = exprType(funcexpr);
 	}
 
+	funcrettype = exprType((Node *) funcexpr->expr);
+
 	/*
 	 * Prepare a resultinfo node for communication.  We always do this
 	 * even if not expecting a set result, so that we can pass
@@ -1087,8 +1116,8 @@ ExecMakeTableFunctionResult(Node *funcexpr,
 
 
 /* ----------------------------------------------------------------
- *		ExecEvalOper
  *		ExecEvalFunc
+ *		ExecEvalOper
  *		ExecEvalDistinct
  *
  *		Evaluate the functional result of a list of arguments by calling the
@@ -1097,80 +1126,49 @@ ExecMakeTableFunctionResult(Node *funcexpr,
  */
 
 /* ----------------------------------------------------------------
- *		ExecEvalOper
+ *		ExecEvalFunc
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalOper(OpExpr *op,
+ExecEvalFunc(FuncExprState *fcache,
 			 ExprContext *econtext,
 			 bool *isNull,
 			 ExprDoneCond *isDone)
 {
-	List	   *argList;
-	FunctionCachePtr fcache;
-
 	/*
-	 * we extract the oid of the function associated with the op and then
-	 * pass the work onto ExecMakeFunctionResult which evaluates the
-	 * arguments and returns the result of calling the function on the
-	 * evaluated arguments.
+	 * Initialize function cache if first time through
 	 */
-	argList = op->args;
-
-	/*
-	 * get the fcache from the OpExpr node. If it is NULL, then initialize
-	 * it
-	 */
-	fcache = op->op_fcache;
-	if (fcache == NULL)
+	if (fcache->func.fn_oid == InvalidOid)
 	{
-		fcache = init_fcache(op->opfuncid, length(argList),
-							 econtext->ecxt_per_query_memory);
-		op->op_fcache = fcache;
+		FuncExpr *func = (FuncExpr *) fcache->xprstate.expr;
+
+		init_fcache(func->funcid, fcache, econtext->ecxt_per_query_memory);
 	}
 
-	return ExecMakeFunctionResult(fcache, argList, econtext,
-								  isNull, isDone);
+	return ExecMakeFunctionResult(fcache, econtext, isNull, isDone);
 }
 
 /* ----------------------------------------------------------------
- *		ExecEvalFunc
+ *		ExecEvalOper
  * ----------------------------------------------------------------
  */
-
 static Datum
-ExecEvalFunc(FuncExpr *func,
+ExecEvalOper(FuncExprState *fcache,
 			 ExprContext *econtext,
 			 bool *isNull,
 			 ExprDoneCond *isDone)
 {
-	List	   *argList;
-	FunctionCachePtr fcache;
-
 	/*
-	 * we extract the oid of the function associated with the func node
-	 * and then pass the work onto ExecMakeFunctionResult which evaluates
-	 * the arguments and returns the result of calling the function on the
-	 * evaluated arguments.
-	 *
-	 * this is nearly identical to the ExecEvalOper code.
+	 * Initialize function cache if first time through
 	 */
-	argList = func->args;
-
-	/*
-	 * get the fcache from the FuncExpr node. If it is NULL, then initialize
-	 * it
-	 */
-	fcache = func->func_fcache;
-	if (fcache == NULL)
+	if (fcache->func.fn_oid == InvalidOid)
 	{
-		fcache = init_fcache(func->funcid, length(argList),
-							 econtext->ecxt_per_query_memory);
-		func->func_fcache = fcache;
+		OpExpr *op = (OpExpr *) fcache->xprstate.expr;
+
+		init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory);
 	}
 
-	return ExecMakeFunctionResult(fcache, argList, econtext,
-								  isNull, isDone);
+	return ExecMakeFunctionResult(fcache, econtext, isNull, isDone);
 }
 
 /* ----------------------------------------------------------------
@@ -1185,34 +1183,31 @@ ExecEvalFunc(FuncExpr *func,
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalDistinct(DistinctExpr *op,
+ExecEvalDistinct(FuncExprState *fcache,
 				 ExprContext *econtext,
 				 bool *isNull,
 				 ExprDoneCond *isDone)
 {
 	Datum		result;
-	FunctionCachePtr fcache;
 	FunctionCallInfoData fcinfo;
 	ExprDoneCond argDone;
 	List	   *argList;
 
 	/*
-	 * extract info from op
+	 * Initialize function cache if first time through
 	 */
-	argList = op->args;
+	if (fcache->func.fn_oid == InvalidOid)
+	{
+		DistinctExpr *op = (DistinctExpr *) fcache->xprstate.expr;
+
+		init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory);
+		Assert(!fcache->func.fn_retset);
+	}
 
 	/*
-	 * get the fcache from the DistinctExpr node. If it is NULL, then
-	 * initialize it
+	 * extract info from fcache
 	 */
-	fcache = op->op_fcache;
-	if (fcache == NULL)
-	{
-		fcache = init_fcache(op->opfuncid, length(argList),
-							 econtext->ecxt_per_query_memory);
-		op->op_fcache = fcache;
-	}
-	Assert(!fcache->func.fn_retset);
+	argList = fcache->args;
 
 	/* Need to prep callinfo structure */
 	MemSet(&fcinfo, 0, sizeof(fcinfo));
@@ -1260,9 +1255,9 @@ ExecEvalDistinct(DistinctExpr *op,
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalNot(BoolExpr *notclause, ExprContext *econtext, bool *isNull)
+ExecEvalNot(BoolExprState *notclause, ExprContext *econtext, bool *isNull)
 {
-	Node	   *clause;
+	ExprState  *clause;
 	Datum		expr_value;
 
 	clause = lfirst(notclause->args);
@@ -1288,7 +1283,7 @@ ExecEvalNot(BoolExpr *notclause, ExprContext *econtext, bool *isNull)
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalOr(BoolExpr *orExpr, ExprContext *econtext, bool *isNull)
+ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext, bool *isNull)
 {
 	List	   *clauses;
 	List	   *clause;
@@ -1314,7 +1309,7 @@ ExecEvalOr(BoolExpr *orExpr, ExprContext *econtext, bool *isNull)
 	 */
 	foreach(clause, clauses)
 	{
-		clause_value = ExecEvalExpr((Node *) lfirst(clause),
+		clause_value = ExecEvalExpr((ExprState *) lfirst(clause),
 									econtext, isNull, NULL);
 
 		/*
@@ -1336,7 +1331,7 @@ ExecEvalOr(BoolExpr *orExpr, ExprContext *econtext, bool *isNull)
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalAnd(BoolExpr *andExpr, ExprContext *econtext, bool *isNull)
+ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, bool *isNull)
 {
 	List	   *clauses;
 	List	   *clause;
@@ -1356,7 +1351,7 @@ ExecEvalAnd(BoolExpr *andExpr, ExprContext *econtext, bool *isNull)
 	 */
 	foreach(clause, clauses)
 	{
-		clause_value = ExecEvalExpr((Node *) lfirst(clause),
+		clause_value = ExecEvalExpr((ExprState *) lfirst(clause),
 									econtext, isNull, NULL);
 
 		/*
@@ -1383,7 +1378,7 @@ ExecEvalAnd(BoolExpr *andExpr, ExprContext *econtext, bool *isNull)
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext,
+ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
 			 bool *isNull, ExprDoneCond *isDone)
 {
 	List	   *clauses;
@@ -1399,9 +1394,9 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext,
 	 */
 	foreach(clause, clauses)
 	{
-		CaseWhen   *wclause = lfirst(clause);
+		CaseWhenState *wclause = lfirst(clause);
 
-		clause_value = ExecEvalExpr((Node *) wclause->expr,
+		clause_value = ExecEvalExpr(wclause->expr,
 									econtext,
 									isNull,
 									NULL);
@@ -1413,7 +1408,7 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext,
 		 */
 		if (DatumGetBool(clause_value) && !*isNull)
 		{
-			return ExecEvalExpr((Node *) wclause->result,
+			return ExecEvalExpr(wclause->result,
 								econtext,
 								isNull,
 								isDone);
@@ -1422,7 +1417,7 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext,
 
 	if (caseExpr->defresult)
 	{
-		return ExecEvalExpr((Node *) caseExpr->defresult,
+		return ExecEvalExpr(caseExpr->defresult,
 							econtext,
 							isNull,
 							isDone);
@@ -1439,14 +1434,19 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext,
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalNullTest(NullTest *ntest,
+ExecEvalNullTest(GenericExprState *nstate,
 				 ExprContext *econtext,
 				 bool *isNull,
 				 ExprDoneCond *isDone)
 {
+	NullTest   *ntest = (NullTest *) nstate->xprstate.expr;
 	Datum		result;
 
-	result = ExecEvalExpr((Node *) ntest->arg, econtext, isNull, isDone);
+	result = ExecEvalExpr(nstate->arg, econtext, isNull, isDone);
+
+	if (isDone && *isDone == ExprEndResult)
+		return result;			/* nothing to check */
+
 	switch (ntest->nulltesttype)
 	{
 		case IS_NULL:
@@ -1479,14 +1479,19 @@ ExecEvalNullTest(NullTest *ntest,
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalBooleanTest(BooleanTest *btest,
+ExecEvalBooleanTest(GenericExprState *bstate,
 					ExprContext *econtext,
 					bool *isNull,
 					ExprDoneCond *isDone)
 {
+	BooleanTest *btest = (BooleanTest *) bstate->xprstate.expr;
 	Datum		result;
 
-	result = ExecEvalExpr((Node *) btest->arg, econtext, isNull, isDone);
+	result = ExecEvalExpr(bstate->arg, econtext, isNull, isDone);
+
+	if (isDone && *isDone == ExprEndResult)
+		return result;			/* nothing to check */
+
 	switch (btest->booltesttype)
 	{
 		case IS_TRUE:
@@ -1560,12 +1565,13 @@ ExecEvalBooleanTest(BooleanTest *btest,
  * datum) otherwise throw an error.
  */
 static Datum
-ExecEvalConstraintTest(ConstraintTest *constraint, ExprContext *econtext,
+ExecEvalConstraintTest(ConstraintTestState *cstate, ExprContext *econtext,
 					   bool *isNull, ExprDoneCond *isDone)
 {
+	ConstraintTest *constraint = (ConstraintTest *) cstate->xprstate.expr;
 	Datum		result;
 
-	result = ExecEvalExpr((Node *) constraint->arg, econtext, isNull, isDone);
+	result = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);
 
 	if (isDone && *isDone == ExprEndResult)
 		return result;			/* nothing to check */
@@ -1596,7 +1602,7 @@ ExecEvalConstraintTest(ConstraintTest *constraint, ExprContext *econtext,
 				econtext->domainValue_datum = result;
 				econtext->domainValue_isNull = *isNull;
 
-				conResult = ExecEvalExpr((Node *) constraint->check_expr,
+				conResult = ExecEvalExpr(cstate->check_expr,
 										 econtext, &conIsNull, NULL);
 
 				if (!conIsNull &&
@@ -1637,17 +1643,21 @@ ExecEvalConstraintTestValue(ConstraintTestValue *conVal, ExprContext *econtext,
  * ----------------------------------------------------------------
  */
 static Datum
-ExecEvalFieldSelect(FieldSelect *fselect,
+ExecEvalFieldSelect(GenericExprState *fstate,
 					ExprContext *econtext,
 					bool *isNull,
 					ExprDoneCond *isDone)
 {
+	FieldSelect *fselect = (FieldSelect *) fstate->xprstate.expr;
 	Datum		result;
 	TupleTableSlot *resSlot;
 
-	result = ExecEvalExpr((Node *) fselect->arg, econtext, isNull, isDone);
+	result = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);
+
+	/* this test covers the isDone exception too: */
 	if (*isNull)
 		return result;
+
 	resSlot = (TupleTableSlot *) DatumGetPointer(result);
 	Assert(resSlot != NULL && IsA(resSlot, TupleTableSlot));
 	result = heap_getattr(resSlot->val,
@@ -1663,7 +1673,7 @@ ExecEvalFieldSelect(FieldSelect *fselect,
  *		Recursively evaluate a targetlist or qualification expression.
  *
  * Inputs:
- *		expression: the expression tree to evaluate
+ *		expression: the expression state tree to evaluate
  *		econtext: evaluation context information
  *
  * Outputs:
@@ -1697,12 +1707,13 @@ ExecEvalFieldSelect(FieldSelect *fselect,
  * ----------------------------------------------------------------
  */
 Datum
-ExecEvalExpr(Node *expression,
+ExecEvalExpr(ExprState *expression,
 			 ExprContext *econtext,
 			 bool *isNull,
 			 ExprDoneCond *isDone)
 {
 	Datum		retDatum;
+	Expr	   *expr;
 
 	/* Set default values for result flags: non-null, not a set result */
 	*isNull = false;
@@ -1720,111 +1731,112 @@ ExecEvalExpr(Node *expression,
 	 * here we dispatch the work to the appropriate type of function given
 	 * the type of our expression.
 	 */
-	switch (nodeTag(expression))
+	expr = expression->expr;
+	switch (nodeTag(expr))
 	{
 		case T_Var:
-			retDatum = ExecEvalVar((Var *) expression, econtext, isNull);
+			retDatum = ExecEvalVar((Var *) expr, econtext, isNull);
 			break;
 		case T_Const:
 			{
-				Const	   *con = (Const *) expression;
+				Const	   *con = (Const *) expr;
 
 				retDatum = con->constvalue;
 				*isNull = con->constisnull;
 				break;
 			}
 		case T_Param:
-			retDatum = ExecEvalParam((Param *) expression, econtext, isNull);
+			retDatum = ExecEvalParam((Param *) expr, econtext, isNull);
 			break;
 		case T_Aggref:
-			retDatum = ExecEvalAggref((Aggref *) expression, econtext, isNull);
+			retDatum = ExecEvalAggref((AggrefExprState *) expression,
+									  econtext,
+									  isNull);
 			break;
 		case T_ArrayRef:
-			retDatum = ExecEvalArrayRef((ArrayRef *) expression,
+			retDatum = ExecEvalArrayRef((ArrayRefExprState *) expression,
 										econtext,
 										isNull,
 										isDone);
 			break;
 		case T_FuncExpr:
-			retDatum = ExecEvalFunc((FuncExpr *) expression, econtext,
+			retDatum = ExecEvalFunc((FuncExprState *) expression, econtext,
 									isNull, isDone);
 			break;
 		case T_OpExpr:
-			retDatum = ExecEvalOper((OpExpr *) expression, econtext,
+			retDatum = ExecEvalOper((FuncExprState *) expression, econtext,
 									isNull, isDone);
 			break;
 		case T_DistinctExpr:
-			retDatum = ExecEvalDistinct((DistinctExpr *) expression, econtext,
+			retDatum = ExecEvalDistinct((FuncExprState *) expression, econtext,
 										isNull, isDone);
 			break;
 		case T_BoolExpr:
 			{
-				BoolExpr   *expr = (BoolExpr *) expression;
+				BoolExprState *state = (BoolExprState *) expression;
 
-				switch (expr->boolop)
+				switch (((BoolExpr *) expr)->boolop)
 				{
 					case AND_EXPR:
-						retDatum = ExecEvalAnd(expr, econtext, isNull);
+						retDatum = ExecEvalAnd(state, econtext, isNull);
 						break;
 					case OR_EXPR:
-						retDatum = ExecEvalOr(expr, econtext, isNull);
+						retDatum = ExecEvalOr(state, econtext, isNull);
 						break;
 					case NOT_EXPR:
-						retDatum = ExecEvalNot(expr, econtext, isNull);
+						retDatum = ExecEvalNot(state, econtext, isNull);
 						break;
 					default:
 						elog(ERROR, "ExecEvalExpr: unknown boolop %d",
-							 expr->boolop);
+							 ((BoolExpr *) expr)->boolop);
 						retDatum = 0;	/* keep compiler quiet */
 						break;
 				}
 				break;
 			}
 		case T_SubPlanExpr:
-			/* XXX temporary hack to find exec state node */
-			retDatum = ExecSubPlan(((SubPlanExpr *) expression)->pstate,
-								   ((SubPlanExpr *) expression)->args,
+			retDatum = ExecSubPlan((SubPlanExprState *) expression,
 								   econtext,
 								   isNull);
 			break;
 		case T_FieldSelect:
-			retDatum = ExecEvalFieldSelect((FieldSelect *) expression,
+			retDatum = ExecEvalFieldSelect((GenericExprState *) expression,
 										   econtext,
 										   isNull,
 										   isDone);
 			break;
 		case T_RelabelType:
-			retDatum = ExecEvalExpr((Node *) ((RelabelType *) expression)->arg,
+			retDatum = ExecEvalExpr(((GenericExprState *) expression)->arg,
 									econtext,
 									isNull,
 									isDone);
 			break;
 		case T_CaseExpr:
-			retDatum = ExecEvalCase((CaseExpr *) expression,
+			retDatum = ExecEvalCase((CaseExprState *) expression,
 									econtext,
 									isNull,
 									isDone);
 			break;
 		case T_NullTest:
-			retDatum = ExecEvalNullTest((NullTest *) expression,
+			retDatum = ExecEvalNullTest((GenericExprState *) expression,
 										econtext,
 										isNull,
 										isDone);
 			break;
 		case T_BooleanTest:
-			retDatum = ExecEvalBooleanTest((BooleanTest *) expression,
+			retDatum = ExecEvalBooleanTest((GenericExprState *) expression,
 										   econtext,
 										   isNull,
 										   isDone);
 			break;
 		case T_ConstraintTest:
-			retDatum = ExecEvalConstraintTest((ConstraintTest *) expression,
+			retDatum = ExecEvalConstraintTest((ConstraintTestState *) expression,
 											  econtext,
 											  isNull,
 											  isDone);
 			break;
 		case T_ConstraintTestValue:
-			retDatum = ExecEvalConstraintTestValue((ConstraintTestValue *) expression,
+			retDatum = ExecEvalConstraintTestValue((ConstraintTestValue *) expr,
 												   econtext,
 												   isNull);
 			break;
@@ -1843,7 +1855,7 @@ ExecEvalExpr(Node *expression,
  * Same as above, but get into the right allocation context explicitly.
  */
 Datum
-ExecEvalExprSwitchContext(Node *expression,
+ExecEvalExprSwitchContext(ExprState *expression,
 						  ExprContext *econtext,
 						  bool *isNull,
 						  ExprDoneCond *isDone)
@@ -1861,166 +1873,301 @@ ExecEvalExprSwitchContext(Node *expression,
 /*
  * ExecInitExpr: prepare an expression tree for execution
  *
+ * This function builds and returns an ExprState tree paralleling the given
+ * Expr node tree.  The ExprState tree can then be handed to ExecEvalExpr
+ * for execution.  Because the Expr tree itself is read-only as far as
+ * ExecInitExpr and ExecEvalExpr are concerned, several different executions
+ * of the same plan tree can occur concurrently.
+ *
+ * This must be called in a memory context that will last as long as repeated
+ * executions of the expression are needed.  Typically the context will be
+ * the same as the per-query context of the associated ExprContext.
+ *
+ * Any Aggref and SubplanExpr nodes found in the tree are added to the lists
+ * of such nodes held by the parent PlanState.  Otherwise, we do very little
+ * initialization here other than building the state-node tree.  Any nontrivial
+ * work associated with initializing runtime info for a node should happen
+ * during the first actual evaluation of that node.  (This policy lets us
+ * avoid work if the node is never actually evaluated.)
+ *
+ * Note: there is no ExecEndExpr function; we assume that any resource
+ * cleanup needed will be handled by just releasing the memory context
+ * in which the state tree is built.  Functions that require additional
+ * cleanup work can register a shutdown callback in the ExprContext.
+ *
  *	'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 SubPlanExpr nodes.
+ *		with a plan.  (If so, it can't have aggs or subplans.)
  */
-Node *
-ExecInitExpr(Node *node, PlanState *parent)
+ExprState *
+ExecInitExpr(Expr *node, PlanState *parent)
 {
-	List	   *temp;
+	ExprState  *state;
 
 	if (node == NULL)
 		return NULL;
 	switch (nodeTag(node))
 	{
 		case T_Var:
-			break;
 		case T_Const:
-			break;
 		case T_Param:
+		case T_ConstraintTestValue:
+			/* No special setup needed for these node types */
+			state = (ExprState *) makeNode(ExprState);
 			break;
 		case T_Aggref:
-			if (parent && IsA(parent, AggState))
 			{
-				AggState   *aggstate = (AggState *) parent;
-				int			naggs;
+				Aggref   *aggref = (Aggref *) node;
+				AggrefExprState *astate = makeNode(AggrefExprState);
 
-				aggstate->aggs = lcons(node, aggstate->aggs);
-				naggs = ++aggstate->numaggs;
+				if (parent && IsA(parent, AggState))
+				{
+					AggState   *aggstate = (AggState *) parent;
+					int			naggs;
 
-				ExecInitExpr((Node *) ((Aggref *) node)->target, parent);
+					aggstate->aggs = lcons(astate, aggstate->aggs);
+					naggs = ++aggstate->numaggs;
 
-				/*
-				 * 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");
+					astate->target = ExecInitExpr(aggref->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");
+				state = (ExprState *) astate;
 			}
-			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((Node *) aref->refexpr, parent);
-				ExecInitExpr((Node *) aref->refassgnexpr, parent);
+				ArrayRefExprState *astate = makeNode(ArrayRefExprState);
+
+				astate->refupperindexpr = (List *)
+					ExecInitExpr((Expr *) aref->refupperindexpr, parent);
+				astate->reflowerindexpr = (List *)
+					ExecInitExpr((Expr *) aref->reflowerindexpr, parent);
+				astate->refexpr = ExecInitExpr(aref->refexpr, parent);
+				astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr,
+													parent);
+				state = (ExprState *) astate;
 			}
 			break;
 		case T_FuncExpr:
 			{
 				FuncExpr   *funcexpr = (FuncExpr *) node;
+				FuncExprState *fstate = makeNode(FuncExprState);
 
-				ExecInitExpr((Node *) funcexpr->args, parent);
+				fstate->args = (List *)
+					ExecInitExpr((Expr *) funcexpr->args, parent);
+				fstate->func.fn_oid = InvalidOid; /* not initialized */
+				state = (ExprState *) fstate;
 			}
 			break;
 		case T_OpExpr:
 			{
 				OpExpr   *opexpr = (OpExpr *) node;
+				FuncExprState *fstate = makeNode(FuncExprState);
 
-				ExecInitExpr((Node *) opexpr->args, parent);
+				fstate->args = (List *)
+					ExecInitExpr((Expr *) opexpr->args, parent);
+				fstate->func.fn_oid = InvalidOid; /* not initialized */
+				state = (ExprState *) fstate;
 			}
 			break;
 		case T_DistinctExpr:
 			{
 				DistinctExpr   *distinctexpr = (DistinctExpr *) node;
+				FuncExprState *fstate = makeNode(FuncExprState);
 
-				ExecInitExpr((Node *) distinctexpr->args, parent);
+				fstate->args = (List *)
+					ExecInitExpr((Expr *) distinctexpr->args, parent);
+				fstate->func.fn_oid = InvalidOid; /* not initialized */
+				state = (ExprState *) fstate;
 			}
 			break;
 		case T_BoolExpr:
 			{
 				BoolExpr   *boolexpr = (BoolExpr *) node;
+				BoolExprState *bstate = makeNode(BoolExprState);
 
-				ExecInitExpr((Node *) boolexpr->args, parent);
+				bstate->args = (List *)
+					ExecInitExpr((Expr *) boolexpr->args, parent);
+				state = (ExprState *) bstate;
 			}
 			break;
 		case T_SubPlanExpr:
 			{
+				/* Keep this in sync with ExecInitExprInitPlan, below */
 				SubPlanExpr *subplanexpr = (SubPlanExpr *) node;
 				SubLink *sublink = subplanexpr->sublink;
+				SubPlanExprState *sstate = makeNode(SubPlanExprState);
 
 				Assert(IsA(sublink, SubLink));
 				if (!parent)
 					elog(ERROR, "ExecInitExpr: SubPlanExpr not expected here");
 
 				/*
-				 * Here we just add the SubPlanExpr nodes to
-				 * parent->subPlan.  Later they will be expanded
-				 * to SubPlanState nodes.
+				 * Here we just add the SubPlanExprState nodes to
+				 * parent->subPlan.  The subplans will be initialized later.
 				 */
-				parent->subPlan = lcons(subplanexpr, parent->subPlan);
+				parent->subPlan = lcons(sstate, parent->subPlan);
+				sstate->planstate = NULL;
+
+				sstate->args = (List *)
+					ExecInitExpr((Expr *) subplanexpr->args, parent);
 
-				/* Must recurse into oper list too */
 				if (sublink->lefthand)
 					elog(ERROR, "ExecInitExpr: sublink has not been transformed");
-				ExecInitExpr((Node *) sublink->oper, parent);
+				sstate->oper = (List *)
+					ExecInitExpr((Expr *) sublink->oper, parent);
 
-				ExecInitExpr((Node *) subplanexpr->args, parent);
+				state = (ExprState *) sstate;
 			}
 			break;
 		case T_FieldSelect:
-			ExecInitExpr((Node *) ((FieldSelect *) node)->arg, parent);
+			{
+				FieldSelect   *fselect = (FieldSelect *) node;
+				GenericExprState *gstate = makeNode(GenericExprState);
+
+				gstate->arg = ExecInitExpr(fselect->arg, parent);
+				state = (ExprState *) gstate;
+			}
 			break;
 		case T_RelabelType:
-			ExecInitExpr((Node *) ((RelabelType *) node)->arg, parent);
+			{
+				RelabelType   *relabel = (RelabelType *) node;
+				GenericExprState *gstate = makeNode(GenericExprState);
+
+				gstate->arg = ExecInitExpr(relabel->arg, parent);
+				state = (ExprState *) gstate;
+			}
 			break;
 		case T_CaseExpr:
 			{
 				CaseExpr   *caseexpr = (CaseExpr *) node;
+				CaseExprState *cstate = makeNode(CaseExprState);
+				List	   *outlist = NIL;
+				List	   *inlist;
 
-				foreach(temp, caseexpr->args)
+				foreach(inlist, caseexpr->args)
 				{
-					CaseWhen   *when = (CaseWhen *) lfirst(temp);
+					CaseWhen   *when = (CaseWhen *) lfirst(inlist);
+					CaseWhenState *wstate = makeNode(CaseWhenState);
 
 					Assert(IsA(when, CaseWhen));
-					ExecInitExpr((Node *) when->expr, parent);
-					ExecInitExpr((Node *) when->result, parent);
+					wstate->xprstate.expr = (Expr *) when;
+					wstate->expr = ExecInitExpr(when->expr, parent);
+					wstate->result = ExecInitExpr(when->result, parent);
+					outlist = lappend(outlist, wstate);
 				}
-				/* caseexpr->arg should be null, but we'll check it anyway */
-				ExecInitExpr((Node *) caseexpr->arg, parent);
-				ExecInitExpr((Node *) caseexpr->defresult, parent);
+				cstate->args = outlist;
+				/* caseexpr->arg should be null by now */
+				Assert(caseexpr->arg == NULL);
+				cstate->defresult = ExecInitExpr(caseexpr->defresult, parent);
+				state = (ExprState *) cstate;
 			}
 			break;
 		case T_NullTest:
-			ExecInitExpr((Node *) ((NullTest *) node)->arg, parent);
+			{
+				NullTest   *ntest = (NullTest *) node;
+				GenericExprState *gstate = makeNode(GenericExprState);
+
+				gstate->arg = ExecInitExpr(ntest->arg, parent);
+				state = (ExprState *) gstate;
+			}
 			break;
 		case T_BooleanTest:
-			ExecInitExpr((Node *) ((BooleanTest *) node)->arg, parent);
+			{
+				BooleanTest   *btest = (BooleanTest *) node;
+				GenericExprState *gstate = makeNode(GenericExprState);
+
+				gstate->arg = ExecInitExpr(btest->arg, parent);
+				state = (ExprState *) gstate;
+			}
 			break;
 		case T_ConstraintTest:
-			ExecInitExpr((Node *) ((ConstraintTest *) node)->arg, parent);
-			ExecInitExpr((Node *) ((ConstraintTest *) node)->check_expr, parent);
-			break;
-		case T_ConstraintTestValue:
+			{
+				ConstraintTest   *ctest = (ConstraintTest *) node;
+				ConstraintTestState *cstate = makeNode(ConstraintTestState);
+
+				cstate->arg = ExecInitExpr(ctest->arg, parent);
+				cstate->check_expr = ExecInitExpr(ctest->check_expr, parent);
+				state = (ExprState *) cstate;
+			}
 			break;
 		case T_TargetEntry:
-			ExecInitExpr((Node *) ((TargetEntry *) node)->expr, parent);
+			{
+				TargetEntry   *tle = (TargetEntry *) node;
+				GenericExprState *gstate = makeNode(GenericExprState);
+
+				gstate->arg = ExecInitExpr(tle->expr, parent);
+				state = (ExprState *) gstate;
+			}
 			break;
 		case T_List:
-			foreach(temp, (List *) node)
 			{
-				ExecInitExpr((Node *) lfirst(temp), parent);
+				List	   *outlist = NIL;
+				List	   *inlist;
+
+				foreach(inlist, (List *) node)
+				{
+					outlist = lappend(outlist,
+									  ExecInitExpr((Expr *) lfirst(inlist),
+												   parent));
+				}
+				/* Don't fall through to the "common" code below */
+				return (ExprState *) outlist;
 			}
-			break;
 		default:
 			elog(ERROR, "ExecInitExpr: unknown expression type %d",
 				 nodeTag(node));
+			state = NULL;		/* keep compiler quiet */
 			break;
 	}
 
-	return node;
+	/* Common code for all state-node types */
+	state->expr = node;
+
+	return state;
+}
+
+/*
+ * ExecInitExprInitPlan --- initialize a subplan expr that's being handled
+ * as an InitPlan.  This is identical to ExecInitExpr's handling of a regular
+ * subplan expr, except we do NOT want to add the node to the parent's
+ * subplan list.
+ */
+SubPlanExprState *
+ExecInitExprInitPlan(SubPlanExpr *node, PlanState *parent)
+{
+	SubLink *sublink = node->sublink;
+	SubPlanExprState *sstate = makeNode(SubPlanExprState);
+
+	Assert(IsA(sublink, SubLink));
+	if (!parent)
+		elog(ERROR, "ExecInitExpr: SubPlanExpr not expected here");
+
+	/* The subplan's state will be initialized later */
+	sstate->planstate = NULL;
+
+	sstate->args = (List *) ExecInitExpr((Expr *) node->args, parent);
+
+	if (sublink->lefthand)
+		elog(ERROR, "ExecInitExpr: sublink has not been transformed");
+
+	sstate->oper = (List *) ExecInitExpr((Expr *) sublink->oper, parent);
+
+	sstate->xprstate.expr = (Expr *) node;
+
+	return sstate;
 }
 
 
@@ -2094,7 +2241,7 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
 
 	foreach(qlist, qual)
 	{
-		Node	   *clause = (Node *) lfirst(qlist);
+		ExprState  *clause = (ExprState *) lfirst(qlist);
 		Datum		expr_value;
 		bool		isNull;
 
@@ -2146,6 +2293,7 @@ ExecCleanTargetListLength(List *targetlist)
 	{
 		TargetEntry *curTle = (TargetEntry *) lfirst(tl);
 
+		Assert(IsA(curTle, TargetEntry));
 		if (!curTle->resdom->resjunk)
 			len++;
 	}
@@ -2180,8 +2328,6 @@ ExecTargetList(List *targetlist,
 	char	   *nulls;
 	ExprDoneCond *itemIsDone;
 	List	   *tl;
-	TargetEntry *tle;
-	AttrNumber	resind;
 	HeapTuple	newTuple;
 	bool		isNull;
 	bool		haveDoneSets;
@@ -2243,11 +2389,11 @@ ExecTargetList(List *targetlist,
 
 	foreach(tl, targetlist)
 	{
-		tle = lfirst(tl);
-
-		resind = tle->resdom->resno - 1;
+		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
+		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
+		AttrNumber	resind = tle->resdom->resno - 1;
 
-		values[resind] = ExecEvalExpr((Node *) tle->expr,
+		values[resind] = ExecEvalExpr(gstate->arg,
 									  econtext,
 									  &isNull,
 									  &itemIsDone[resind]);
@@ -2295,29 +2441,26 @@ ExecTargetList(List *targetlist,
 			 */
 			foreach(tl, targetlist)
 			{
-				tle = lfirst(tl);
+				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
+				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
+				AttrNumber	resind = tle->resdom->resno - 1;
 
-				if (tle->resdom != NULL)
+				if (itemIsDone[resind] == ExprEndResult)
 				{
-					resind = tle->resdom->resno - 1;
+					values[resind] = ExecEvalExpr(gstate->arg,
+												  econtext,
+												  &isNull,
+												  &itemIsDone[resind]);
+					nulls[resind] = isNull ? 'n' : ' ';
 
 					if (itemIsDone[resind] == ExprEndResult)
 					{
-						values[resind] = ExecEvalExpr((Node *) tle->expr,
-													  econtext,
-													  &isNull,
-													&itemIsDone[resind]);
-						nulls[resind] = isNull ? 'n' : ' ';
-
-						if (itemIsDone[resind] == ExprEndResult)
-						{
-							/*
-							 * Oh dear, this item is returning an empty
-							 * set. Guess we can't make a tuple after all.
-							 */
-							*isDone = ExprEndResult;
-							break;
-						}
+						/*
+						 * Oh dear, this item is returning an empty
+						 * set. Guess we can't make a tuple after all.
+						 */
+						*isDone = ExprEndResult;
+						break;
 					}
 				}
 			}
@@ -2326,24 +2469,23 @@ ExecTargetList(List *targetlist,
 			 * If we cannot make a tuple because some sets are empty, we
 			 * still have to cycle the nonempty sets to completion, else
 			 * resources will not be released from subplans etc.
+			 *
+			 * XXX is that still necessary?
 			 */
 			if (*isDone == ExprEndResult)
 			{
 				foreach(tl, targetlist)
 				{
-					tle = lfirst(tl);
+					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
+					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
+					AttrNumber	resind = tle->resdom->resno - 1;
 
-					if (tle->resdom != NULL)
+					while (itemIsDone[resind] == ExprMultipleResult)
 					{
-						resind = tle->resdom->resno - 1;
-
-						while (itemIsDone[resind] == ExprMultipleResult)
-						{
-							(void) ExecEvalExpr((Node *) tle->expr,
-												econtext,
-												&isNull,
-												&itemIsDone[resind]);
-						}
+						(void) ExecEvalExpr(gstate->arg,
+											econtext,
+											&isNull,
+											&itemIsDone[resind]);
 					}
 				}
 
diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c
index eecc108fb1c..2e7291a006d 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.62 2002/12/12 15:49:28 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.63 2002/12/13 19:45:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -550,6 +550,7 @@ ExecInitNullTupleSlot(EState *estate, TupleDesc tupType)
  *		ExecTypeFromTL
  *
  *		Generate a tuple descriptor for the result tuple of a targetlist.
+ *		(A parse/plan tlist must be passed, not an ExprState tlist.)
  *		Note that resjunk columns, if any, are included in the result.
  *
  *		Currently there are about 4 different places where we create
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 0dd17b7965b..36997a49103 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.91 2002/12/05 15:50:32 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.92 2002/12/13 19:45:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -321,9 +321,9 @@ ExecAssignResultTypeFromTL(PlanState *planstate)
 	}
 
 	/*
-	 * 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.
+	 * ExecTypeFromTL needs the parse-time representation of the tlist, not
+	 * a list of ExprStates.  This is good because some plan nodes don't
+	 * bother to set up planstate->targetlist ...
 	 */
 	tupDesc = ExecTypeFromTL(planstate->plan->targetlist, hasoid);
 	ExecAssignResultType(planstate, tupDesc, true);
@@ -681,7 +681,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 			continue;
 
 		indexInfo = indexInfoArray[i];
-		predicate = indexInfo->ii_Predicate;
+		predicate = indexInfo->ii_PredicateState;
 		if (predicate != NIL)
 		{
 			/* Skip this index-update if the predicate isn't satisfied */
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index 73e4a8044ef..e6ba3887630 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.99 2002/12/12 15:49:24 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.100 2002/12/13 19:45:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -82,7 +82,8 @@ typedef struct AggStatePerAggData
 	 * thereafter:
 	 */
 
-	/* Link to Aggref node this working state is for */
+	/* Links to Aggref expr and state nodes this working state is for */
+	AggrefExprState *aggrefstate;
 	Aggref	   *aggref;
 
 	/* Oids of transfer functions */
@@ -237,7 +238,7 @@ initialize_aggregates(AggState *aggstate,
 	{
 		AggStatePerAgg peraggstate = &peragg[aggno];
 		AggStatePerGroup pergroupstate = &pergroup[aggno];
-		Aggref	   *aggref = peraggstate->aggref;
+		Aggref *aggref = peraggstate->aggref;
 
 		/*
 		 * Start a fresh sort operation for each DISTINCT aggregate.
@@ -411,11 +412,12 @@ advance_aggregates(AggState *aggstate, AggStatePerGroup pergroup)
 	{
 		AggStatePerAgg peraggstate = &aggstate->peragg[aggno];
 		AggStatePerGroup pergroupstate = &pergroup[aggno];
+		AggrefExprState *aggrefstate = peraggstate->aggrefstate;
 		Aggref	   *aggref = peraggstate->aggref;
 		Datum		newVal;
 		bool		isNull;
 
-		newVal = ExecEvalExprSwitchContext((Node *) aggref->target, econtext,
+		newVal = ExecEvalExprSwitchContext(aggrefstate->target, econtext,
 										   &isNull, NULL);
 
 		if (aggref->aggdistinct)
@@ -1145,10 +1147,10 @@ ExecInitAgg(Agg *node, EState *estate)
 	 * particular order.
 	 */
 	aggstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Node *) node->plan.targetlist,
+		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
 	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Node *) node->plan.qual,
+		ExecInitExpr((Expr *) node->plan.qual,
 					 (PlanState *) aggstate);
 
 	/*
@@ -1227,7 +1229,8 @@ ExecInitAgg(Agg *node, EState *estate)
 	aggno = -1;
 	foreach(alist, aggstate->aggs)
 	{
-		Aggref	   *aggref = (Aggref *) lfirst(alist);
+		AggrefExprState *aggrefstate = (AggrefExprState *) lfirst(alist);
+		Aggref	   *aggref = (Aggref *) aggrefstate->xprstate.expr;
 		AggStatePerAgg peraggstate = &peragg[++aggno];
 		HeapTuple	aggTuple;
 		Form_pg_aggregate aggform;
@@ -1236,10 +1239,11 @@ ExecInitAgg(Agg *node, EState *estate)
 					finalfn_oid;
 		Datum		textInitVal;
 
-		/* Mark Aggref node with its associated index in the result array */
-		aggref->aggno = aggno;
+		/* Mark Aggref state node with assigned index in the result array */
+		aggrefstate->aggno = aggno;
 
 		/* Fill in the peraggstate data */
+		peraggstate->aggrefstate = aggrefstate;
 		peraggstate->aggref = aggref;
 
 		aggTuple = SearchSysCache(AGGFNOID,
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index f6df416d0f9..d0bf78631da 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.14 2002/12/05 15:50:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.15 2002/12/13 19:45:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -172,10 +172,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate)
 	 * initialize child expressions
 	 */
 	scanstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Node *) node->scan.plan.targetlist,
+		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
 	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Node *) node->scan.plan.qual,
+		ExecInitExpr((Expr *) node->scan.plan.qual,
 					 (PlanState *) scanstate);
 
 	/*
@@ -241,7 +241,8 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate)
 	 * Other node-specific setup
 	 */
 	scanstate->tuplestorestate = NULL;
-	scanstate->funcexpr = rte->funcexpr;
+	scanstate->funcexpr = ExecInitExpr((Expr *) rte->funcexpr,
+									   (PlanState *) scanstate);
 
 	scanstate->ss.ps.ps_TupFromTlist = false;
 
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index 3d562a476b8..8bb72ba3438 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.51 2002/12/05 15:50:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.52 2002/12/13 19:45:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -168,10 +168,10 @@ ExecInitGroup(Group *node, EState *estate)
 	 * initialize child expressions
 	 */
 	grpstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Node *) node->plan.targetlist,
+		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
 	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Node *) node->plan.qual,
+		ExecInitExpr((Expr *) node->plan.qual,
 					 (PlanState *) grpstate);
 
 	/*
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9448ee466a8..45ba826317d 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.69 2002/12/05 15:50:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.70 2002/12/13 19:45:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -76,7 +76,7 @@ ExecHash(HashState *node)
 	/*
 	 * set expression context
 	 */
-	hashkeys = ((Hash *) node->ps.plan)->hashkeys;
+	hashkeys = node->hashkeys;
 	econtext = node->ps.ps_ExprContext;
 
 	/*
@@ -138,10 +138,10 @@ ExecInitHash(Hash *node, EState *estate)
 	 * initialize child expressions
 	 */
 	hashstate->ps.targetlist = (List *)
-		ExecInitExpr((Node *) node->plan.targetlist,
+		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
 	hashstate->ps.qual = (List *)
-		ExecInitExpr((Node *) node->plan.qual,
+		ExecInitExpr((Expr *) node->plan.qual,
 					 (PlanState *) hashstate);
 
 	/*
@@ -554,7 +554,8 @@ ExecHashGetBucket(HashJoinTable hashtable,
 		/*
 		 * Get the join attribute value of the tuple
 		 */
-		keyval = ExecEvalExpr(lfirst(hk), econtext, &isNull, NULL);
+		keyval = ExecEvalExpr((ExprState *) lfirst(hk),
+							  econtext, &isNull, NULL);
 
 		/*
 		 * Compute the hash function
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 06796c590e3..07de8703812 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.43 2002/12/05 15:50:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.44 2002/12/13 19:45:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -169,7 +169,7 @@ ExecHashJoin(HashJoinState *node)
 			 * for this tuple from the hash table
 			 */
 			node->hj_CurBucketNo = ExecHashGetBucket(hashtable, econtext,
-														outerkeys);
+													 outerkeys);
 			node->hj_CurTuple = NULL;
 
 			/*
@@ -302,6 +302,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate)
 	HashJoinState *hjstate;
 	Plan	   *outerNode;
 	Hash	   *hashNode;
+	List	   *hclauses;
 	List	   *hcl;
 
 	/*
@@ -322,17 +323,17 @@ ExecInitHashJoin(HashJoin *node, EState *estate)
 	 * initialize child expressions
 	 */
 	hjstate->js.ps.targetlist = (List *)
-		ExecInitExpr((Node *) node->join.plan.targetlist,
+		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
 	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Node *) node->join.plan.qual,
+		ExecInitExpr((Expr *) node->join.plan.qual,
 					 (PlanState *) hjstate);
 	hjstate->js.jointype = node->join.jointype;
 	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Node *) node->join.joinqual,
+		ExecInitExpr((Expr *) node->join.joinqual,
 					 (PlanState *) hjstate);
 	hjstate->hashclauses = (List *)
-		ExecInitExpr((Node *) node->hashclauses,
+		ExecInitExpr((Expr *) node->hashclauses,
 					 (PlanState *) hjstate);
 
 	/*
@@ -402,15 +403,23 @@ ExecInitHashJoin(HashJoin *node, EState *estate)
 
 	/*
 	 * The planner already made a list of the inner hashkeys for us,
-	 * but we also need a list of the outer hashkeys.
+	 * but we also need a list of the outer hashkeys.  Each list of
+	 * exprs must then be prepared for execution.
 	 */
-	hjstate->hj_InnerHashKeys = hashNode->hashkeys;
-	hjstate->hj_OuterHashKeys = NIL;
+	hjstate->hj_InnerHashKeys = (List *)
+		ExecInitExpr((Expr *) hashNode->hashkeys,
+					 innerPlanState(hjstate));
+	((HashState *) innerPlanState(hjstate))->hashkeys =
+		hjstate->hj_InnerHashKeys;
+
+	hclauses = NIL;
 	foreach(hcl, node->hashclauses)
 	{
-		hjstate->hj_OuterHashKeys = lappend(hjstate->hj_OuterHashKeys,
-											get_leftop(lfirst(hcl)));
+		hclauses = lappend(hclauses, get_leftop(lfirst(hcl)));
 	}
+	hjstate->hj_OuterHashKeys = (List *)
+		ExecInitExpr((Expr *) hclauses,
+					 (PlanState *) hjstate);
 
 	hjstate->js.ps.ps_OuterTupleSlot = NULL;
 	hjstate->js.ps.ps_TupFromTlist = false;
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 0112d3641de..1e36e93113d 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.73 2002/12/12 15:49:24 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.74 2002/12/13 19:45:52 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -283,7 +283,7 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
 	int			numIndices;
 	IndexScanDescPtr scanDescs;
 	ScanKey    *scanKeys;
-	int		  **runtimeKeyInfo;
+	ExprState ***runtimeKeyInfo;
 	int		   *numScanKeys;
 	Index		scanrelid;
 	int			i;
@@ -328,29 +328,18 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
 	 */
 	if (runtimeKeyInfo)
 	{
-		List	   *indxqual;
-
-		indxqual = node->indxqual;
 		for (i = 0; i < numIndices; i++)
 		{
-			List	   *qual = lfirst(indxqual);
 			int			n_keys;
 			ScanKey		scan_keys;
-			int		   *run_keys;
-			List	   *listscan;
+			ExprState **run_keys;
 
-			indxqual = lnext(indxqual);
 			n_keys = numScanKeys[i];
 			scan_keys = scanKeys[i];
 			run_keys = runtimeKeyInfo[i];
 
-			listscan = qual;
 			for (j = 0; j < n_keys; j++)
 			{
-				Expr	   *clause = lfirst(listscan);
-
-				listscan = lnext(listscan);
-
 				/*
 				 * If we have a run-time key, then extract the run-time
 				 * expression and evaluate it with respect to the current
@@ -364,17 +353,12 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
 				 * is wrong, we could copy the result into our context
 				 * explicitly, but I think that's not necessary...
 				 */
-				if (run_keys[j] != NO_OP)
+				if (run_keys[j] != NULL)
 				{
-					Node	   *scanexpr;
 					Datum		scanvalue;
 					bool		isNull;
 
-					scanexpr = (run_keys[j] == RIGHT_OP) ?
-						(Node *) get_rightop(clause) :
-						(Node *) get_leftop(clause);
-
-					scanvalue = ExecEvalExprSwitchContext(scanexpr,
+					scanvalue = ExecEvalExprSwitchContext(run_keys[j],
 														  econtext,
 														  &isNull,
 														  NULL);
@@ -424,7 +408,7 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt)
 void
 ExecEndIndexScan(IndexScanState *node)
 {
-	int		  **runtimeKeyInfo;
+	ExprState ***runtimeKeyInfo;
 	ScanKey    *scanKeys;
 	int		   *numScanKeys;
 	int			numIndices;
@@ -585,7 +569,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 	int		   *numScanKeys;
 	RelationPtr indexDescs;
 	IndexScanDescPtr scanDescs;
-	int		  **runtimeKeyInfo;
+	ExprState ***runtimeKeyInfo;
 	bool		have_runtime_keys;
 	RangeTblEntry *rtentry;
 	Index		relid;
@@ -610,16 +594,16 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 	 * initialize child expressions
 	 */
 	indexstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Node *) node->scan.plan.targetlist,
+		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
 	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Node *) node->scan.plan.qual,
+		ExecInitExpr((Expr *) node->scan.plan.qual,
 					 (PlanState *) indexstate);
 	indexstate->indxqual = (List *)
-		ExecInitExpr((Node *) node->indxqual,
+		ExecInitExpr((Expr *) node->indxqual,
 					 (PlanState *) indexstate);
 	indexstate->indxqualorig = (List *)
-		ExecInitExpr((Node *) node->indxqualorig,
+		ExecInitExpr((Expr *) node->indxqualorig,
 					 (PlanState *) indexstate);
 
 #define INDEXSCAN_NSLOTS 2
@@ -672,7 +656,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 	 * initialize space for runtime key info (may not be needed)
 	 */
 	have_runtime_keys = false;
-	runtimeKeyInfo = (int **) palloc(numIndices * sizeof(int *));
+	runtimeKeyInfo = (ExprState ***) palloc0(numIndices * sizeof(ExprState **));
 
 	/*
 	 * build the index scan keys from the index qualification
@@ -684,15 +668,15 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 		List	   *qual;
 		int			n_keys;
 		ScanKey		scan_keys;
-		int		   *run_keys;
+		ExprState **run_keys;
 
 		qual = lfirst(indxqual);
 		indxqual = lnext(indxqual);
 		n_keys = length(qual);
 		scan_keys = (n_keys <= 0) ? (ScanKey) NULL :
 			(ScanKey) palloc(n_keys * sizeof(ScanKeyData));
-		run_keys = (n_keys <= 0) ? (int *) NULL :
-			(int *) palloc(n_keys * sizeof(int));
+		run_keys = (n_keys <= 0) ? (ExprState **) NULL :
+			(ExprState **) palloc(n_keys * sizeof(ExprState *));
 
 		CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext);
 
@@ -704,8 +688,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 		for (j = 0; j < n_keys; j++)
 		{
 			OpExpr	   *clause; /* one clause of index qual */
-			Node	   *leftop; /* expr on lhs of operator */
-			Node	   *rightop;	/* expr on rhs ... */
+			Expr	   *leftop; /* expr on lhs of operator */
+			Expr	   *rightop;	/* expr on rhs ... */
 			bits16		flags = 0;
 
 			int			scanvar;	/* which var identifies varattno */
@@ -740,9 +724,9 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 			 * which case we need to recalculate the index scan key at run
 			 * time.
 			 *
-			 * Hence, we set have_runtime_keys to true and then set the
-			 * appropriate flag in run_keys to LEFT_OP or RIGHT_OP. The
-			 * corresponding scan keys are recomputed at run time.
+			 * Hence, we set have_runtime_keys to true and place the
+			 * appropriate subexpression in run_keys. The corresponding
+			 * scan key values are recomputed at run time.
 			 *
 			 * XXX Although this code *thinks* it can handle an indexqual
 			 * with the indexkey on either side, in fact it cannot.
@@ -760,19 +744,20 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 			 */
 
 			scanvar = NO_OP;
-			run_keys[j] = NO_OP;
+			run_keys[j] = NULL;
 
 			/*
 			 * determine information in leftop
 			 */
-			leftop = (Node *) get_leftop((Expr *) clause);
+			leftop = (Expr *) get_leftop((Expr *) clause);
 
 			if (leftop && IsA(leftop, RelabelType))
-				leftop = (Node *) ((RelabelType *) leftop)->arg;
+				leftop = ((RelabelType *) leftop)->arg;
 
 			Assert(leftop != NULL);
 
-			if (IsA(leftop, Var) &&var_is_rel((Var *) leftop))
+			if (IsA(leftop, Var) &&
+				var_is_rel((Var *) leftop))
 			{
 				/*
 				 * if the leftop is a "rel-var", then it means that it is
@@ -792,32 +777,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 				if (((Const *) leftop)->constisnull)
 					flags |= SK_ISNULL;
 			}
-			else if (IsA(leftop, Param))
-			{
-				bool		isnull;
-
-				/*
-				 * if the leftop is a Param node then it means it
-				 * identifies the value to place in our scan key.
-				 */
-
-				/* Life was so easy before ... subselects */
-				if (((Param *) leftop)->paramkind == PARAM_EXEC)
-				{
-					/* treat Param as runtime key */
-					have_runtime_keys = true;
-					run_keys[j] = LEFT_OP;
-				}
-				else
-				{
-					/* treat Param like a constant */
-					scanvalue = ExecEvalParam((Param *) leftop,
-										indexstate->ss.ps.ps_ExprContext,
-											  &isnull);
-					if (isnull)
-						flags |= SK_ISNULL;
-				}
-			}
 			else
 			{
 				/*
@@ -826,20 +785,21 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 				 * key.
 				 */
 				have_runtime_keys = true;
-				run_keys[j] = LEFT_OP;
+				run_keys[j] = ExecInitExpr(leftop, (PlanState *) indexstate);
 			}
 
 			/*
 			 * now determine information in rightop
 			 */
-			rightop = (Node *) get_rightop((Expr *) clause);
+			rightop = (Expr *) get_rightop((Expr *) clause);
 
 			if (rightop && IsA(rightop, RelabelType))
-				rightop = (Node *) ((RelabelType *) rightop)->arg;
+				rightop = ((RelabelType *) rightop)->arg;
 
 			Assert(rightop != NULL);
 
-			if (IsA(rightop, Var) &&var_is_rel((Var *) rightop))
+			if (IsA(rightop, Var) &&
+				var_is_rel((Var *) rightop))
 			{
 				/*
 				 * here we make sure only one op identifies the
@@ -867,32 +827,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 				if (((Const *) rightop)->constisnull)
 					flags |= SK_ISNULL;
 			}
-			else if (IsA(rightop, Param))
-			{
-				bool		isnull;
-
-				/*
-				 * if the rightop is a Param node then it means it
-				 * identifies the value to place in our scan key.
-				 */
-
-				/* Life was so easy before ... subselects */
-				if (((Param *) rightop)->paramkind == PARAM_EXEC)
-				{
-					/* treat Param as runtime key */
-					have_runtime_keys = true;
-					run_keys[j] = RIGHT_OP;
-				}
-				else
-				{
-					/* treat Param like a constant */
-					scanvalue = ExecEvalParam((Param *) rightop,
-										indexstate->ss.ps.ps_ExprContext,
-											  &isnull);
-					if (isnull)
-						flags |= SK_ISNULL;
-				}
-			}
 			else
 			{
 				/*
@@ -901,7 +835,7 @@ ExecInitIndexScan(IndexScan *node, EState *estate)
 				 * key.
 				 */
 				have_runtime_keys = true;
-				run_keys[j] = RIGHT_OP;
+				run_keys[j] = ExecInitExpr(rightop, (PlanState *) indexstate);
 			}
 
 			/*
diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c
index 1ea3aa62b52..6abd83de8aa 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.12 2002/12/05 15:50:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeLimit.c,v 1.13 2002/12/13 19:45:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -301,9 +301,9 @@ ExecInitLimit(Limit *node, EState *estate)
 	/*
 	 * initialize child expressions
 	 */
-	limitstate->limitOffset = ExecInitExpr(node->limitOffset,
+	limitstate->limitOffset = ExecInitExpr((Expr *) node->limitOffset,
 										   (PlanState *) limitstate);
-	limitstate->limitCount = ExecInitExpr(node->limitCount,
+	limitstate->limitCount = ExecInitExpr((Expr *) node->limitCount,
 										  (PlanState *) limitstate);
 
 #define LIMIT_NSLOTS 1
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index e6c2c86be19..1bb5878d819 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.53 2002/12/12 15:49:25 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.54 2002/12/13 19:45:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -101,23 +101,26 @@ static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
  * ----------------------------------------------------------------
  */
 static void
-MJFormSkipQuals(List *qualList, List **ltQuals, List **gtQuals)
+MJFormSkipQuals(List *qualList, List **ltQuals, List **gtQuals,
+				PlanState *parent)
 {
-	List	   *ltcdr,
+	List	   *ltexprs,
+			   *gtexprs,
+			   *ltcdr,
 			   *gtcdr;
 
 	/*
 	 * Make modifiable copies of the qualList.
 	 */
-	*ltQuals = (List *) copyObject((Node *) qualList);
-	*gtQuals = (List *) copyObject((Node *) qualList);
+	ltexprs = (List *) copyObject((Node *) qualList);
+	gtexprs = (List *) copyObject((Node *) qualList);
 
 	/*
 	 * Scan both lists in parallel, so that we can update the operators
 	 * with the minimum number of syscache searches.
 	 */
-	ltcdr = *ltQuals;
-	foreach(gtcdr, *gtQuals)
+	ltcdr = ltexprs;
+	foreach(gtcdr, gtexprs)
 	{
 		OpExpr	   *ltop = (OpExpr *) lfirst(ltcdr);
 		OpExpr	   *gtop = (OpExpr *) lfirst(gtcdr);
@@ -137,11 +140,15 @@ MJFormSkipQuals(List *qualList, List **ltQuals, List **gtQuals)
 							  &gtop->opno,
 							  &ltop->opfuncid,
 							  &gtop->opfuncid);
-		ltop->op_fcache = NULL;
-		gtop->op_fcache = NULL;
 
 		ltcdr = lnext(ltcdr);
 	}
+
+	/*
+	 * Prepare both lists for execution.
+	 */
+	*ltQuals = (List *) ExecInitExpr((Expr *) ltexprs, parent);
+	*gtQuals = (List *) ExecInitExpr((Expr *) gtexprs, parent);
 }
 
 /* ----------------------------------------------------------------
@@ -193,8 +200,10 @@ MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
 		 *
 		 * A NULL result is considered false.
 		 */
-		const_value = ExecEvalExpr((Node *) lfirst(clause), econtext,
-								   &isNull, NULL);
+		const_value = ExecEvalExpr((ExprState *) lfirst(clause),
+								   econtext,
+								   &isNull,
+								   NULL);
 
 		if (DatumGetBool(const_value) && !isNull)
 		{
@@ -208,7 +217,7 @@ MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
 		 * key1 = key2 so we move on to the next pair of keys.
 		 *-----------
 		 */
-		const_value = ExecEvalExpr((Node *) lfirst(eqclause),
+		const_value = ExecEvalExpr((ExprState *) lfirst(eqclause),
 								   econtext,
 								   &isNull,
 								   NULL);
@@ -1409,17 +1418,17 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate)
 	 * initialize child expressions
 	 */
 	mergestate->js.ps.targetlist = (List *)
-		ExecInitExpr((Node *) node->join.plan.targetlist,
+		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
 	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Node *) node->join.plan.qual,
+		ExecInitExpr((Expr *) node->join.plan.qual,
 					 (PlanState *) mergestate);
 	mergestate->js.jointype = node->join.jointype;
 	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Node *) node->join.joinqual,
+		ExecInitExpr((Expr *) node->join.joinqual,
 					 (PlanState *) mergestate);
 	mergestate->mergeclauses = (List *)
-		ExecInitExpr((Node *) node->mergeclauses,
+		ExecInitExpr((Expr *) node->mergeclauses,
 					 (PlanState *) mergestate);
 
 	/*
@@ -1492,7 +1501,8 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate)
 	 */
 	MJFormSkipQuals(node->mergeclauses,
 					&mergestate->mj_OuterSkipQual,
-					&mergestate->mj_InnerSkipQual);
+					&mergestate->mj_InnerSkipQual,
+					(PlanState *) mergestate);
 
 	MJ_printf("\nExecInitMergeJoin: OuterSkipQual is ");
 	MJ_nodeDisplay(mergestate->mj_OuterSkipQual);
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index ae2061aa969..452ed7d70c3 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.27 2002/12/05 15:50:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.28 2002/12/13 19:45:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -286,14 +286,14 @@ ExecInitNestLoop(NestLoop *node, EState *estate)
 	 * initialize child expressions
 	 */
 	nlstate->js.ps.targetlist = (List *)
-		ExecInitExpr((Node *) node->join.plan.targetlist,
+		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
 	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Node *) node->join.plan.qual,
+		ExecInitExpr((Expr *) node->join.plan.qual,
 					 (PlanState *) nlstate);
 	nlstate->js.jointype = node->join.jointype;
 	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Node *) node->join.joinqual,
+		ExecInitExpr((Expr *) node->join.joinqual,
 					 (PlanState *) nlstate);
 
 	/*
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index f14fd01c424..3f2c9927e01 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.22 2002/12/05 15:50:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.23 2002/12/13 19:45:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -205,12 +205,12 @@ ExecInitResult(Result *node, EState *estate)
 	 * initialize child expressions
 	 */
 	resstate->ps.targetlist = (List *)
-		ExecInitExpr((Node *) node->plan.targetlist,
+		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
 	resstate->ps.qual = (List *)
-		ExecInitExpr((Node *) node->plan.qual,
+		ExecInitExpr((Expr *) node->plan.qual,
 					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr(node->resconstantqual,
+	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
 											 (PlanState *) resstate);
 
 	/*
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index e19098fba23..6a7393795b2 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.39 2002/12/05 15:50:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.40 2002/12/13 19:45:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -207,10 +207,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate)
 	 * initialize child expressions
 	 */
 	scanstate->ps.targetlist = (List *)
-		ExecInitExpr((Node *) node->plan.targetlist,
+		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
 	scanstate->ps.qual = (List *)
-		ExecInitExpr((Node *) node->plan.qual,
+		ExecInitExpr((Expr *) node->plan.qual,
 					 (PlanState *) scanstate);
 
 #define SEQSCAN_NSLOTS 2
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 880a6233db2..69fcc58e079 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.36 2002/12/12 15:49:27 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.37 2002/12/13 19:45:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,11 +30,12 @@
  * ----------------------------------------------------------------
  */
 Datum
-ExecSubPlan(SubPlanState *node, List *pvar,
-			ExprContext *econtext, bool *isNull)
+ExecSubPlan(SubPlanExprState *node,
+			ExprContext *econtext,
+			bool *isNull)
 {
 	PlanState  *planstate = node->planstate;
-	SubPlanExpr *subplan = (SubPlanExpr *) node->ps.plan;
+	SubPlanExpr *subplan = (SubPlanExpr *) node->xprstate.expr;
 	SubLink    *sublink = subplan->sublink;
 	SubLinkType subLinkType = sublink->subLinkType;
 	bool		useor = sublink->useor;
@@ -42,6 +43,7 @@ ExecSubPlan(SubPlanState *node, List *pvar,
 	TupleTableSlot *slot;
 	Datum		result;
 	bool		found = false;	/* TRUE if got at least one subplan tuple */
+	List	   *pvar;
 	List	   *lst;
 
 	/*
@@ -56,6 +58,7 @@ ExecSubPlan(SubPlanState *node, List *pvar,
 	/*
 	 * Set Params of this plan from parent plan correlation Vars
 	 */
+	pvar = node->args;
 	if (subplan->parParam != NIL)
 	{
 		foreach(lst, subplan->parParam)
@@ -64,7 +67,7 @@ ExecSubPlan(SubPlanState *node, List *pvar,
 
 			prm = &(econtext->ecxt_param_exec_vals[lfirsti(lst)]);
 			Assert(pvar != NIL);
-			prm->value = ExecEvalExprSwitchContext((Node *) lfirst(pvar),
+			prm->value = ExecEvalExprSwitchContext((ExprState *) lfirst(pvar),
 												   econtext,
 												   &(prm->isnull),
 												   NULL);
@@ -149,9 +152,10 @@ ExecSubPlan(SubPlanState *node, List *pvar,
 		 * For ALL, ANY, and MULTIEXPR sublinks, iterate over combining
 		 * operators for columns of tuple.
 		 */
-		foreach(lst, sublink->oper)
+		foreach(lst, node->oper)
 		{
-			OpExpr	   *expr = (OpExpr *) lfirst(lst);
+			ExprState  *exprstate = (ExprState *) lfirst(lst);
+			OpExpr	   *expr = (OpExpr *) exprstate->expr;
 			Param	   *prm = lsecond(expr->args);
 			ParamExecData *prmdata;
 			Datum		expresult;
@@ -194,7 +198,7 @@ ExecSubPlan(SubPlanState *node, List *pvar,
 			/*
 			 * Now we can eval the combining operator for this column.
 			 */
-			expresult = ExecEvalExprSwitchContext((Node *) expr, econtext,
+			expresult = ExecEvalExprSwitchContext(exprstate, econtext,
 												  &expnull, NULL);
 
 			/*
@@ -287,64 +291,57 @@ ExecSubPlan(SubPlanState *node, List *pvar,
  *		ExecInitSubPlan
  * ----------------------------------------------------------------
  */
-SubPlanState *
-ExecInitSubPlan(SubPlanExpr *node, EState *estate)
+void
+ExecInitSubPlan(SubPlanExprState *sstate, EState *estate)
 {
-	SubPlanState *subplanstate;
+	SubPlanExpr *expr = (SubPlanExpr *) sstate->xprstate.expr;
 	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);
+	ExecCheckRTPerms(expr->rtable, CMD_SELECT);
 
 	/*
-	 * create state structure
+	 * initialize state
 	 */
-	subplanstate = makeNode(SubPlanState);
-	subplanstate->ps.plan = (Plan *) node;
-	subplanstate->ps.state = estate;
-
-	subplanstate->needShutdown = false;
-	subplanstate->curTuple = NULL;
-
-	/* XXX temporary hack */
-	node->pstate = subplanstate;
+	sstate->needShutdown = false;
+	sstate->curTuple = NULL;
 
 	/*
 	 * create an EState for the subplan
 	 */
 	sp_estate = CreateExecutorState();
 
-	sp_estate->es_range_table = node->rtable;
+	sp_estate->es_range_table = expr->rtable;
 	sp_estate->es_param_list_info = estate->es_param_list_info;
 	sp_estate->es_param_exec_vals = estate->es_param_exec_vals;
 	sp_estate->es_tupleTable =
-		ExecCreateTupleTable(ExecCountSlotsNode(node->plan) + 10);
+		ExecCreateTupleTable(ExecCountSlotsNode(expr->plan) + 10);
 	sp_estate->es_snapshot = estate->es_snapshot;
 	sp_estate->es_instrument = estate->es_instrument;
 
 	/*
 	 * Start up the subplan
 	 */
-	subplanstate->planstate = ExecInitNode(node->plan, sp_estate);
+	sstate->planstate = ExecInitNode(expr->plan, sp_estate);
 
-	subplanstate->needShutdown = true;	/* now we need to shutdown the subplan */
+	sstate->needShutdown = true;	/* now we need to shutdown the subplan */
 
 	/*
 	 * If this plan is un-correlated or undirect correlated one and want
 	 * to set params for parent plan then prepare parameters.
 	 */
-	if (node->setParam != NIL)
+	if (expr->setParam != NIL)
 	{
 		List	   *lst;
 
-		foreach(lst, node->setParam)
+		foreach(lst, expr->setParam)
 		{
 			ParamExecData *prm = &(estate->es_param_exec_vals[lfirsti(lst)]);
 
-			prm->execPlan = subplanstate;
+			prm->execPlan = sstate;
 		}
 
 		/*
@@ -353,8 +350,6 @@ ExecInitSubPlan(SubPlanExpr *node, EState *estate)
 		 * it, for others - it doesn't matter...
 		 */
 	}
-
-	return subplanstate;
 }
 
 /* ----------------------------------------------------------------
@@ -371,12 +366,11 @@ ExecInitSubPlan(SubPlanExpr *node, EState *estate)
  * ----------------------------------------------------------------
  */
 void
-ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
+ExecSetParamPlan(SubPlanExprState *node, ExprContext *econtext)
 {
 	PlanState  *planstate = node->planstate;
-	SubPlanExpr *subplan = (SubPlanExpr *) node->ps.plan;
-	SubLink    *sublink = subplan->sublink;
-	EState	   *estate = node->ps.state;
+	SubPlanExpr *subplan = (SubPlanExpr *) node->xprstate.expr;
+	SubLinkType	subLinkType = subplan->sublink->subLinkType;
 	MemoryContext oldcontext;
 	TupleTableSlot *slot;
 	List	   *lst;
@@ -388,8 +382,8 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 	 */
 	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
 
-	if (sublink->subLinkType == ANY_SUBLINK ||
-		sublink->subLinkType == ALL_SUBLINK)
+	if (subLinkType == ANY_SUBLINK ||
+		subLinkType == ALL_SUBLINK)
 		elog(ERROR, "ExecSetParamPlan: ANY/ALL subselect unsupported");
 
 	if (planstate->chgParam != NULL)
@@ -403,9 +397,9 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 		TupleDesc	tdesc = slot->ttc_tupleDescriptor;
 		int			i = 1;
 
-		if (sublink->subLinkType == EXISTS_SUBLINK)
+		if (subLinkType == EXISTS_SUBLINK)
 		{
-			ParamExecData *prm = &(estate->es_param_exec_vals[lfirsti(subplan->setParam)]);
+			ParamExecData *prm = &(econtext->ecxt_param_exec_vals[lfirsti(subplan->setParam)]);
 
 			prm->execPlan = NULL;
 			prm->value = BoolGetDatum(true);
@@ -415,8 +409,8 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 		}
 
 		if (found &&
-			(sublink->subLinkType == EXPR_SUBLINK ||
-			 sublink->subLinkType == MULTIEXPR_SUBLINK))
+			(subLinkType == EXPR_SUBLINK ||
+			 subLinkType == MULTIEXPR_SUBLINK))
 			elog(ERROR, "More than one tuple returned by a subselect used as an expression.");
 
 		found = true;
@@ -434,7 +428,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 
 		foreach(lst, subplan->setParam)
 		{
-			ParamExecData *prm = &(estate->es_param_exec_vals[lfirsti(lst)]);
+			ParamExecData *prm = &(econtext->ecxt_param_exec_vals[lfirsti(lst)]);
 
 			prm->execPlan = NULL;
 			prm->value = heap_getattr(tup, i, tdesc, &(prm->isnull));
@@ -444,9 +438,9 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 
 	if (!found)
 	{
-		if (sublink->subLinkType == EXISTS_SUBLINK)
+		if (subLinkType == EXISTS_SUBLINK)
 		{
-			ParamExecData *prm = &(estate->es_param_exec_vals[lfirsti(subplan->setParam)]);
+			ParamExecData *prm = &(econtext->ecxt_param_exec_vals[lfirsti(subplan->setParam)]);
 
 			prm->execPlan = NULL;
 			prm->value = BoolGetDatum(false);
@@ -456,7 +450,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
 		{
 			foreach(lst, subplan->setParam)
 			{
-				ParamExecData *prm = &(estate->es_param_exec_vals[lfirsti(lst)]);
+				ParamExecData *prm = &(econtext->ecxt_param_exec_vals[lfirsti(lst)]);
 
 				prm->execPlan = NULL;
 				prm->value = (Datum) 0;
@@ -479,7 +473,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
  * ----------------------------------------------------------------
  */
 void
-ExecEndSubPlan(SubPlanState *node)
+ExecEndSubPlan(SubPlanExprState *node)
 {
 	if (node->needShutdown)
 	{
@@ -494,11 +488,11 @@ ExecEndSubPlan(SubPlanState *node)
 }
 
 void
-ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent)
+ExecReScanSetParamPlan(SubPlanExprState *node, PlanState *parent)
 {
 	PlanState  *planstate = node->planstate;
-	SubPlanExpr *subplan = (SubPlanExpr *) node->ps.plan;
-	EState	   *estate = node->ps.state;
+	SubPlanExpr *subplan = (SubPlanExpr *) node->xprstate.expr;
+	EState	   *estate = parent->state;
 	List	   *lst;
 
 	if (subplan->parParam != NULL)
@@ -509,8 +503,7 @@ ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent)
 		elog(ERROR, "ExecReScanSetParamPlan: extParam list of plan is NULL");
 
 	/*
-	 * Don't actual re-scan: ExecSetParamPlan does re-scan if
-	 * subplan->plan->chgParam is not NULL... ExecReScan (planstate, NULL);
+	 * Don't actually re-scan: ExecSetParamPlan does it if needed.
 	 */
 
 	/*
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index ba0ed8635b7..68291ba6e34 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.14 2002/12/05 15:50:33 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.15 2002/12/13 19:45:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -131,10 +131,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate)
 	 * initialize child expressions
 	 */
 	subquerystate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Node *) node->scan.plan.targetlist,
+		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
 	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Node *) node->scan.plan.qual,
+		ExecInitExpr((Expr *) node->scan.plan.qual,
 					 (PlanState *) subquerystate);
 
 #define SUBQUERYSCAN_NSLOTS 1
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 53b3c26d35a..ba2793407ce 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.28 2002/12/05 15:50:34 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.29 2002/12/13 19:45:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -380,10 +380,10 @@ ExecInitTidScan(TidScan *node, EState *estate)
 	 * initialize child expressions
 	 */
 	tidstate->ss.ps.targetlist = (List *)
-		ExecInitExpr((Node *) node->scan.plan.targetlist,
+		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
 	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Node *) node->scan.plan.qual,
+		ExecInitExpr((Expr *) node->scan.plan.qual,
 					 (PlanState *) tidstate);
 
 #define TIDSCAN_NSLOTS 2
@@ -404,7 +404,10 @@ ExecInitTidScan(TidScan *node, EState *estate)
 	 * get the tid node information
 	 */
 	tidList = (ItemPointerData *) palloc(length(node->tideval) * sizeof(ItemPointerData));
-	numTids = TidListCreate(node->tideval,
+	tidstate->tss_tideval = (List *)
+		ExecInitExpr((Expr *) node->tideval,
+					 (PlanState *) tidstate);
+	numTids = TidListCreate(tidstate->tss_tideval,
 							tidstate->ss.ps.ps_ExprContext,
 							tidList);
 	tidPtr = -1;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 258e1a1188c..d84e6061246 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.231 2002/12/12 20:35:12 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.232 2002/12/13 19:45:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -710,7 +710,6 @@ _copyAggref(Aggref *from)
 	COPY_NODE_FIELD(target);
 	COPY_SCALAR_FIELD(aggstar);
 	COPY_SCALAR_FIELD(aggdistinct);
-	COPY_SCALAR_FIELD(aggno);	/* will go away soon */
 
 	return newnode;
 }
@@ -750,9 +749,6 @@ _copyFuncExpr(FuncExpr *from)
 	COPY_SCALAR_FIELD(funcformat);
 	COPY_NODE_FIELD(args);
 
-	/* Do not copy the run-time state, if any */
-	newnode->func_fcache = NULL;
-
 	return newnode;
 }
 
@@ -770,9 +766,6 @@ _copyOpExpr(OpExpr *from)
 	COPY_SCALAR_FIELD(opretset);
 	COPY_NODE_FIELD(args);
 
-	/* Do not copy the run-time state, if any */
-	newnode->op_fcache = NULL;
-
 	return newnode;
 }
 
@@ -790,9 +783,6 @@ _copyDistinctExpr(DistinctExpr *from)
 	COPY_SCALAR_FIELD(opretset);
 	COPY_NODE_FIELD(args);
 
-	/* Do not copy the run-time state, if any */
-	newnode->op_fcache = NULL;
-
 	return newnode;
 }
 
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 299c910cc3b..6c09574e13c 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/nodeFuncs.c,v 1.20 2002/12/12 15:49:28 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/nodeFuncs.c,v 1.21 2002/12/13 19:45:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -88,5 +88,4 @@ set_opfuncid(OpExpr *opexpr)
 {
 	if (opexpr->opfuncid == InvalidOid)
 		opexpr->opfuncid = get_opcode(opexpr->opno);
-	opexpr->op_fcache = NULL;		/* XXX will go away soon */
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 9ae71555095..a688b471e79 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.188 2002/12/12 20:35:12 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.189 2002/12/13 19:45:56 tgl Exp $
  *
  * NOTES
  *	  Every node type that can appear in stored rules' parsetrees *must*
@@ -214,8 +214,6 @@ _outDatum(StringInfo str, Datum value, int typlen, bool typbyval)
 
 /*
  * print the basic stuff of all nodes that inherit from Plan
- *
- * NOTE: we deliberately omit the execution state (EState)
  */
 static void
 _outPlanInfo(StringInfo str, Plan *node)
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index ba46a4f088c..54e3916ebf6 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -8,12 +8,13 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.142 2002/12/12 20:35:12 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.143 2002/12/13 19:45:56 tgl Exp $
  *
  * NOTES
  *	  Path and Plan nodes do not have any readfuncs support, because we
  *	  never have occasion to read them in.  (There was once code here that
- *	  claimed to read them, but it was broken as well as unused.)
+ *	  claimed to read them, but it was broken as well as unused.)  We
+ *	  never read executor state trees, either.
  *
  *-------------------------------------------------------------------------
  */
@@ -439,8 +440,6 @@ _readFuncExpr(void)
 	READ_ENUM_FIELD(funcformat, CoercionForm);
 	READ_NODE_FIELD(args);
 
-	local_node->func_fcache = NULL;
-
 	READ_DONE();
 }
 
@@ -468,8 +467,6 @@ _readOpExpr(void)
 	READ_BOOL_FIELD(opretset);
 	READ_NODE_FIELD(args);
 
-	local_node->op_fcache = NULL;
-
 	READ_DONE();
 }
 
@@ -497,8 +494,6 @@ _readDistinctExpr(void)
 	READ_BOOL_FIELD(opretset);
 	READ_NODE_FIELD(args);
 
-	local_node->op_fcache = NULL;
-
 	READ_DONE();
 }
 
diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 7a20de8c1c1..e4eedd11790 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.127 2002/12/12 15:49:28 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.128 2002/12/13 19:45:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1124,6 +1124,7 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
 				clause_strategy,
 				test_strategy;
 	Expr	   *test_expr;
+	ExprState  *test_exprstate;
 	Datum		test_result;
 	bool		isNull;
 	Relation	relation;
@@ -1274,9 +1275,10 @@ pred_test_simple_clause(Expr *predicate, Node *clause)
 							  (Expr *) clause_const,
 							  (Expr *) pred_const);
 	set_opfuncid((OpExpr *) test_expr);
+	test_exprstate = ExecInitExpr(test_expr, NULL);
 
-	econtext = MakeExprContext(NULL, TransactionCommandContext);
-	test_result = ExecEvalExprSwitchContext((Node *) test_expr, econtext,
+	econtext = MakeExprContext(NULL, CurrentMemoryContext);
+	test_result = ExecEvalExprSwitchContext(test_exprstate, econtext,
 											&isNull, NULL);
 	FreeExprContext(econtext);
 
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 83c05a5d5fd..af96615c7fc 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.117 2002/12/12 20:35:12 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.118 2002/12/13 19:45:56 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -1315,7 +1315,18 @@ eval_const_expressions_mutator(Node *node, List *active_fns)
 			simple = simplify_function(expr->opfuncid, args,
 									   false, active_fns);
 			if (simple)			/* successfully simplified it */
-				return (Node *) simple;
+			{
+				/*
+				 * Since the underlying operator is "=", must negate its
+				 * result
+				 */
+				Const  *csimple = (Const *) simple;
+
+				Assert(IsA(csimple, Const));
+				csimple->constvalue =
+					BoolGetDatum(!DatumGetBool(csimple->constvalue));
+				return (Node *) csimple;
+			}
 		}
 
 		/*
@@ -1672,6 +1683,7 @@ evaluate_function(Oid funcid, List *args, HeapTuple func_tuple)
 	bool		has_nonconst_input = false;
 	bool		has_null_input = false;
 	FuncExpr   *newexpr;
+	ExprState  *newexprstate;
 	ExprContext *econtext;
 	Datum		const_val;
 	bool		const_is_null;
@@ -1738,7 +1750,9 @@ evaluate_function(Oid funcid, List *args, HeapTuple func_tuple)
 	 */
 	econtext = MakeExprContext(NULL, CurrentMemoryContext);
 
-	const_val = ExecEvalExprSwitchContext((Node *) newexpr, econtext,
+	newexprstate = ExecInitExpr((Expr *) newexpr, NULL);
+
+	const_val = ExecEvalExprSwitchContext(newexprstate, econtext,
 										  &const_is_null, NULL);
 
 	/* Must copy result out of sub-context used by expression eval */
@@ -1746,7 +1760,6 @@ evaluate_function(Oid funcid, List *args, HeapTuple func_tuple)
 		const_val = datumCopy(const_val, resultTypByVal, resultTypLen);
 
 	FreeExprContext(econtext);
-	pfree(newexpr);
 
 	/*
 	 * Make the constant result node.
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a7ead103188..83a322cf148 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *	$Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.256 2002/12/12 20:35:12 tgl Exp $
+ *	$Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.257 2002/12/13 19:45:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,7 +23,6 @@
 #include "commands/prepare.h"
 #include "nodes/makefuncs.h"
 #include "optimizer/clauses.h"
-#include "optimizer/planmain.h"
 #include "parser/analyze.h"
 #include "parser/gramparse.h"
 #include "parser/parsetree.h"
@@ -2424,8 +2423,6 @@ transformExecuteStmt(ParseState *pstate, ExecuteStmt *stmt)
 					 format_type_be(given_type_id),
 					 format_type_be(expected_type_id));
 
-			fix_opfuncids(expr);
-
 			lfirst(l) = expr;
 
 			paramtypes = lnext(paramtypes);
diff --git a/src/backend/utils/adt/sets.c b/src/backend/utils/adt/sets.c
index 3c4838c0fe7..6cb06735b15 100644
--- a/src/backend/utils/adt/sets.c
+++ b/src/backend/utils/adt/sets.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.54 2002/09/04 20:31:29 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.55 2002/12/13 19:45:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,7 +24,6 @@
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_proc.h"
 #include "executor/executor.h"
-#include "utils/fcache.h"
 #include "utils/fmgroids.h"
 #include "utils/sets.h"
 #include "utils/syscache.h"
@@ -143,7 +142,7 @@ Datum
 seteval(PG_FUNCTION_ARGS)
 {
 	Oid			funcoid = PG_GETARG_OID(0);
-	FunctionCachePtr fcache;
+	FuncExprState *fcache;
 	Datum		result;
 	bool		isNull;
 	ExprDoneCond isDone;
@@ -152,10 +151,27 @@ seteval(PG_FUNCTION_ARGS)
 	 * If this is the first call, we need to set up the fcache for the
 	 * target set's function.
 	 */
-	fcache = (FunctionCachePtr) fcinfo->flinfo->fn_extra;
+	fcache = (FuncExprState *) fcinfo->flinfo->fn_extra;
 	if (fcache == NULL)
 	{
-		fcache = init_fcache(funcoid, 0, fcinfo->flinfo->fn_mcxt);
+		MemoryContext	oldcontext;
+		FuncExpr   *func;
+
+		oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt);
+
+		func = makeNode(FuncExpr);
+		func->funcid = funcoid;
+		func->funcresulttype = InvalidOid; /* nothing will look at this */
+		func->funcretset = true;
+		func->funcformat = COERCE_EXPLICIT_CALL;
+		func->args = NIL;		/* there are no arguments */
+
+		fcache = (FuncExprState *) ExecInitExpr((Expr *) func, NULL);
+
+		MemoryContextSwitchTo(oldcontext);
+
+		init_fcache(funcoid, fcache, fcinfo->flinfo->fn_mcxt);
+
 		fcinfo->flinfo->fn_extra = (void *) fcache;
 	}
 
@@ -169,22 +185,10 @@ seteval(PG_FUNCTION_ARGS)
 	isDone = ExprSingleResult;
 
 	result = ExecMakeFunctionResult(fcache,
-									NIL,
 									NULL,		/* no econtext, see above */
 									&isNull,
 									&isDone);
 
-	/*
-	 * If we're done with the results of this set function, get rid of its
-	 * func cache so that we will start from the top next time. (Can you
-	 * say "memory leak"?  This feature is a crock anyway...)
-	 */
-	if (isDone != ExprMultipleResult)
-	{
-		pfree(fcache);
-		fcinfo->flinfo->fn_extra = NULL;
-	}
-
 	/*
 	 * Return isNull/isDone status.
 	 */
diff --git a/src/backend/utils/cache/Makefile b/src/backend/utils/cache/Makefile
index eb9d3e89c0c..b13ecc38ddc 100644
--- a/src/backend/utils/cache/Makefile
+++ b/src/backend/utils/cache/Makefile
@@ -4,7 +4,7 @@
 #    Makefile for utils/cache
 #
 # IDENTIFICATION
-#    $Header: /cvsroot/pgsql/src/backend/utils/cache/Makefile,v 1.16 2002/03/31 06:26:31 tgl Exp $
+#    $Header: /cvsroot/pgsql/src/backend/utils/cache/Makefile,v 1.17 2002/12/13 19:45:56 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -12,8 +12,7 @@ subdir = src/backend/utils/cache
 top_builddir = ../../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = catcache.o inval.o relcache.o syscache.o lsyscache.o \
-	fcache.o
+OBJS = catcache.o inval.o relcache.o syscache.o lsyscache.o
 
 all: SUBSYS.o
 
diff --git a/src/backend/utils/cache/fcache.c b/src/backend/utils/cache/fcache.c
deleted file mode 100644
index 0ad615fc803..00000000000
--- a/src/backend/utils/cache/fcache.c
+++ /dev/null
@@ -1,53 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fcache.c
- *	  Code for the 'function cache' used in Oper and Func nodes.
- *
- *
- * 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/utils/cache/Attic/fcache.c,v 1.45 2002/06/20 20:29:39 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include "postgres.h"
-
-#include "miscadmin.h"
-#include "utils/acl.h"
-#include "utils/fcache.h"
-#include "utils/lsyscache.h"
-
-
-/*
- * Build a 'FunctionCache' struct given the PG_PROC oid.
- */
-FunctionCachePtr
-init_fcache(Oid foid, int nargs, MemoryContext fcacheCxt)
-{
-	FunctionCachePtr retval;
-	AclResult	aclresult;
-
-	/* Check permission to call function */
-	aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE);
-	if (aclresult != ACLCHECK_OK)
-		aclcheck_error(aclresult, get_func_name(foid));
-
-	/* Safety check (should never fail, as parser should check sooner) */
-	if (nargs > FUNC_MAX_ARGS)
-		elog(ERROR, "init_fcache: too many arguments");
-
-	/* Create fcache entry in the desired context */
-	retval = (FunctionCachePtr) MemoryContextAlloc(fcacheCxt,
-												   sizeof(FunctionCache));
-	MemSet(retval, 0, sizeof(FunctionCache));
-
-	/* Set up the primary fmgr lookup information */
-	fmgr_info_cxt(foid, &(retval->func), fcacheCxt);
-
-	/* Initialize additional info */
-	retval->setArgsValid = false;
-
-	return retval;
-}
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index df3e52d4f0f..bdcb3a6afa5 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.81 2002/12/05 15:50:36 tgl Exp $
+ * $Id: executor.h,v 1.82 2002/12/13 19:45:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -73,26 +73,27 @@ extern TupleDesc ExecGetTupType(PlanState *node);
 /*
  * prototypes from functions in execQual.c
  */
-extern Datum ExecEvalParam(Param *expression, ExprContext *econtext,
-			  bool *isNull);
 extern Datum GetAttributeByNum(TupleTableSlot *slot, AttrNumber attrno,
 				  bool *isNull);
 extern Datum GetAttributeByName(TupleTableSlot *slot, char *attname,
 				   bool *isNull);
-extern Datum ExecMakeFunctionResult(FunctionCachePtr fcache,
-					   List *arguments,
+extern void init_fcache(Oid foid, FuncExprState *fcache,
+						MemoryContext fcacheCxt);
+extern Datum ExecMakeFunctionResult(FuncExprState *fcache,
 					   ExprContext *econtext,
 					   bool *isNull,
 					   ExprDoneCond *isDone);
-extern Tuplestorestate *ExecMakeTableFunctionResult(Node *funcexpr,
+extern Tuplestorestate *ExecMakeTableFunctionResult(ExprState *funcexpr,
 							ExprContext *econtext,
 							TupleDesc expectedDesc,
 							TupleDesc *returnDesc);
-extern Datum ExecEvalExpr(Node *expression, ExprContext *econtext,
+extern Datum ExecEvalExpr(ExprState *expression, ExprContext *econtext,
 			 bool *isNull, ExprDoneCond *isDone);
-extern Datum ExecEvalExprSwitchContext(Node *expression, ExprContext *econtext,
+extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext,
 						  bool *isNull, ExprDoneCond *isDone);
-extern Node *ExecInitExpr(Node *node, PlanState *parent);
+extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
+extern SubPlanExprState *ExecInitExprInitPlan(SubPlanExpr *node,
+											  PlanState *parent);
 extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
diff --git a/src/include/executor/nodeSubplan.h b/src/include/executor/nodeSubplan.h
index e025c9de696..97f4b66bcd6 100644
--- a/src/include/executor/nodeSubplan.h
+++ b/src/include/executor/nodeSubplan.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: nodeSubplan.h,v 1.13 2002/12/12 15:49:40 tgl Exp $
+ * $Id: nodeSubplan.h,v 1.14 2002/12/13 19:45:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -16,12 +16,13 @@
 
 #include "nodes/execnodes.h"
 
-extern SubPlanState *ExecInitSubPlan(SubPlanExpr *node, EState *estate);
-extern Datum ExecSubPlan(SubPlanState *node, List *pvar, ExprContext *econtext,
-			bool *isNull);
-extern void ExecEndSubPlan(SubPlanState *node);
-extern void ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent);
+extern void ExecInitSubPlan(SubPlanExprState *sstate, EState *estate);
+extern Datum ExecSubPlan(SubPlanExprState *node,
+						 ExprContext *econtext,
+						 bool *isNull);
+extern void ExecEndSubPlan(SubPlanExprState *node);
+extern void ExecReScanSetParamPlan(SubPlanExprState *node, PlanState *parent);
 
-extern void ExecSetParamPlan(SubPlanState *node, ExprContext *econtext);
+extern void ExecSetParamPlan(SubPlanExprState *node, ExprContext *econtext);
 
 #endif   /* NODESUBPLAN_H */
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index 1ff389e8511..263661492da 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -2,7 +2,7 @@
  *
  * spi.h
  *
- * $Id: spi.h,v 1.33 2001/11/08 20:37:52 momjian Exp $
+ * $Id: spi.h,v 1.34 2002/12/13 19:45:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -30,7 +30,6 @@
 #include "tcop/utility.h"
 #include "tcop/dest.h"
 #include "nodes/params.h"
-#include "utils/fcache.h"
 #include "utils/datum.h"
 #include "utils/syscache.h"
 #include "utils/builtins.h"
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index db8e431c769..905bfae6d87 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.83 2002/12/12 20:35:13 tgl Exp $
+ * $Id: execnodes.h,v 1.84 2002/12/13 19:45:57 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -36,6 +36,7 @@
  *							(ie, number of attrs from underlying relation)
  *		KeyAttrNumbers		underlying-rel attribute numbers used as keys
  *		Predicate			partial-index predicate, or NIL if none
+ *		PredicateState		exec state for predicate, or NIL if none
  *		FuncOid				OID of function, or InvalidOid if not f. index
  *		FuncInfo			fmgr lookup data for function, if FuncOid valid
  *		Unique				is it a unique index?
@@ -47,7 +48,8 @@ typedef struct IndexInfo
 	int			ii_NumIndexAttrs;
 	int			ii_NumKeyAttrs;
 	AttrNumber	ii_KeyAttrNumbers[INDEX_MAX_KEYS];
-	List	   *ii_Predicate;
+	List	   *ii_Predicate;	/* list of Expr */
+	List	   *ii_PredicateState; /* list of ExprState */
 	Oid			ii_FuncOid;
 	FmgrInfo	ii_FuncInfo;
 	bool		ii_Unique;
@@ -254,7 +256,7 @@ typedef struct JunkFilter
  *		IndexRelationInfo		array of key/attr info for indices
  *		TrigDesc				triggers to be fired, if any
  *		TrigFunctions			cached lookup info for trigger functions
- *		ConstraintExprs			array of constraint-checking expressions
+ *		ConstraintExprs			array of constraint-checking expr states
  *		junkFilter				for removing junk attributes from tuples
  * ----------------
  */
@@ -332,7 +334,178 @@ typedef struct EState
 
 
 /* ----------------------------------------------------------------
- *				 Executor State Information
+ *				 Expression State Trees
+ *
+ * Each executable expression tree has a parallel ExprState tree.
+ *
+ * Unlike PlanState, there is not an exact one-for-one correspondence between
+ * ExprState node types and Expr node types.  Many Expr node types have no
+ * need for node-type-specific run-time state, and so they can use plain
+ * ExprState or GenericExprState as their associated ExprState node type.
+ * ----------------------------------------------------------------
+ */
+
+/* ----------------
+ *		ExprState node
+ *
+ * ExprState is the common superclass for all ExprState-type nodes.
+ *
+ * It can also be instantiated directly for leaf Expr nodes that need no
+ * local run-time state (such as Var, Const, or Param).
+ * ----------------
+ */
+typedef struct ExprState
+{
+	NodeTag		type;
+	Expr	   *expr;			/* associated Expr node */
+} ExprState;
+
+/* ----------------
+ *		GenericExprState node
+ *
+ * This is used for Expr node types that need no local run-time state,
+ * but have one child Expr node.
+ * ----------------
+ */
+typedef struct GenericExprState
+{
+	ExprState	xprstate;
+	ExprState  *arg;			/* state of my child node */
+} GenericExprState;
+
+/* ----------------
+ *		AggrefExprState node
+ * ----------------
+ */
+typedef struct AggrefExprState
+{
+	ExprState	xprstate;
+	ExprState  *target;			/* state of my child node */
+	int			aggno;			/* ID number for agg within its plan node */
+} AggrefExprState;
+
+/* ----------------
+ *		ArrayRefExprState node
+ * ----------------
+ */
+typedef struct ArrayRefExprState
+{
+	ExprState	xprstate;
+	List	   *refupperindexpr; /* states for child nodes */
+	List	   *reflowerindexpr;
+	ExprState  *refexpr;
+	ExprState  *refassgnexpr;
+} ArrayRefExprState;
+
+/* ----------------
+ *		FuncExprState node
+ *
+ * Although named for FuncExpr, this is also used for OpExpr and DistinctExpr
+ * nodes; be careful to check what xprstate.expr is actually pointing at!
+ * ----------------
+ */
+typedef struct FuncExprState
+{
+	ExprState	xprstate;
+	List	   *args;			/* states of argument expressions */
+
+	/*
+	 * Function manager's lookup info for the target function.  If func.fn_oid
+	 * is InvalidOid, we haven't initialized it yet.
+	 */
+	FmgrInfo	func;
+
+	/*
+	 * We also need to store argument values across calls when evaluating a
+	 * function-returning-set.
+	 *
+	 * setArgsValid is true when we are evaluating a set-valued function
+	 * and we are in the middle of a call series; we want to pass the same
+	 * argument values to the function again (and again, until it returns
+	 * ExprEndResult).
+	 */
+	bool		setArgsValid;
+
+	/*
+	 * Flag to remember whether we found a set-valued argument to the
+	 * function. This causes the function result to be a set as well.
+	 * Valid only when setArgsValid is true.
+	 */
+	bool		setHasSetArg;	/* some argument returns a set */
+
+	/*
+	 * Current argument data for a set-valued function; contains valid
+	 * data only if setArgsValid is true.
+	 */
+	FunctionCallInfoData setArgs;
+} FuncExprState;
+
+/* ----------------
+ *		BoolExprState node
+ * ----------------
+ */
+typedef struct BoolExprState
+{
+	ExprState	xprstate;
+	List	   *args;			/* states of argument expression(s) */
+} BoolExprState;
+
+/* ----------------
+ *		SubPlanExprState node
+ *
+ * Note: there is no separate ExprState node for the SubLink.  All it would
+ * need is the oper field, which we can just as easily put here.
+ * ----------------
+ */
+typedef struct SubPlanExprState
+{
+	ExprState	xprstate;
+	struct PlanState *planstate; /* subselect plan's state tree */
+	bool		needShutdown;	/* TRUE = need to shutdown subplan */
+	HeapTuple	curTuple;		/* copy of most recent tuple from subplan */
+	List	   *args;			/* states of argument expression(s) */
+	List	   *oper;			/* states for executable combining exprs */
+} SubPlanExprState;
+
+/* ----------------
+ *		CaseExprState node
+ * ----------------
+ */
+typedef struct CaseExprState
+{
+	ExprState	xprstate;
+	List	   *args;			/* the arguments (list of WHEN clauses) */
+	ExprState  *defresult;		/* the default result (ELSE clause) */
+} CaseExprState;
+
+/* ----------------
+ *		CaseWhenState node
+ * ----------------
+ */
+typedef struct CaseWhenState
+{
+	ExprState	xprstate;
+	ExprState  *expr;			/* condition expression */
+	ExprState  *result;			/* substitution result */
+} CaseWhenState;
+
+/* ----------------
+ *		ConstraintTestState node
+ * ----------------
+ */
+typedef struct ConstraintTestState
+{
+	ExprState	xprstate;
+	ExprState  *arg;			/* input expression */
+	ExprState  *check_expr;		/* for CHECK test, a boolean expression */
+} ConstraintTestState;
+
+
+/* ----------------------------------------------------------------
+ *				 Executor State Trees
+ *
+ * An executing query has a PlanState tree paralleling the Plan tree
+ * that describes the plan.
  * ----------------------------------------------------------------
  */
 
@@ -365,9 +538,9 @@ typedef struct PlanState
 	List	   *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree;	/* input plan tree(s) */
 	struct PlanState *righttree;
-	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated
+	List	   *initPlan;		/* Init SubPlanExprState nodes (un-correlated
 								 * expr subselects) */
-	List	   *subPlan;		/* SubPlanState nodes in my expressions */
+	List	   *subPlan;		/* SubPlanExprState nodes in my expressions */
 
 	/*
 	 * State for management of parameter-change-driven rescanning
@@ -403,7 +576,7 @@ typedef struct PlanState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	Node	   *resconstantqual;
+	ExprState  *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -467,7 +640,8 @@ typedef ScanState SeqScanState;
  *		IndexPtr		   current index in use
  *		ScanKeys		   Skey structures to scan index rels
  *		NumScanKeys		   array of no of keys in each Skey struct
- *		RuntimeKeyInfo	   array of array of flags for Skeys evaled at runtime
+ *		RuntimeKeyInfo	   array of array of exprstates for Skeys
+ *						   that will be evaluated at runtime
  *		RuntimeContext	   expr context for evaling runtime Skeys
  *		RuntimeKeysReady   true if runtime Skeys have been computed
  *		RelationDescs	   ptr to array of relation descriptors
@@ -484,7 +658,7 @@ typedef struct IndexScanState
 	int			iss_MarkIndexPtr;
 	ScanKey    *iss_ScanKeys;
 	int		   *iss_NumScanKeys;
-	int		  **iss_RuntimeKeyInfo;
+	ExprState ***iss_RuntimeKeyInfo;
 	ExprContext *iss_RuntimeContext;
 	bool		iss_RuntimeKeysReady;
 	RelationPtr iss_RelationDescs;
@@ -502,6 +676,7 @@ typedef struct IndexScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
+	List	   *tss_tideval;	/* list of ExprState nodes */
 	int			tss_NumTids;
 	int			tss_TidPtr;
 	int			tss_MarkTidPtr;
@@ -542,7 +717,7 @@ typedef struct FunctionScanState
 	ScanState	ss;				/* its first field is NodeTag */
 	TupleDesc	tupdesc;
 	Tuplestorestate *tuplestorestate;
-	Node	   *funcexpr;
+	ExprState  *funcexpr;
 } FunctionScanState;
 
 /* ----------------------------------------------------------------
@@ -597,9 +772,9 @@ typedef struct NestLoopState
 typedef struct MergeJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *mergeclauses;
-	List	   *mj_OuterSkipQual;
-	List	   *mj_InnerSkipQual;
+	List	   *mergeclauses;		/* list of ExprState nodes */
+	List	   *mj_OuterSkipQual;	/* list of ExprState nodes */
+	List	   *mj_InnerSkipQual;	/* list of ExprState nodes */
 	int			mj_JoinState;
 	bool		mj_MatchedOuter;
 	bool		mj_MatchedInner;
@@ -632,12 +807,12 @@ typedef struct MergeJoinState
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;
+	List	   *hashclauses;	/* list of ExprState nodes */
 	HashJoinTable hj_HashTable;
 	int			hj_CurBucketNo;
 	HashJoinTuple hj_CurTuple;
-	List	   *hj_OuterHashKeys;
-	List	   *hj_InnerHashKeys;
+	List	   *hj_OuterHashKeys;	/* list of ExprState nodes */
+	List	   *hj_InnerHashKeys;	/* list of ExprState nodes */
 	TupleTableSlot *hj_OuterTupleSlot;
 	TupleTableSlot *hj_HashTupleSlot;
 	TupleTableSlot *hj_NullInnerTupleSlot;
@@ -757,6 +932,8 @@ typedef struct HashState
 {
 	PlanState	ps;				/* its first field is NodeTag */
 	HashJoinTable hashtable;	/* hash table for the hashjoin */
+	List	   *hashkeys;		/* list of ExprState nodes */
+	/* hashkeys is same as parent's hj_InnerHashKeys */
 } HashState;
 
 /* ----------------
@@ -804,8 +981,8 @@ typedef enum
 typedef struct LimitState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	Node	   *limitOffset;	/* OFFSET parameter, or NULL if none */
-	Node	   *limitCount;		/* COUNT parameter, or NULL if none */
+	ExprState  *limitOffset;	/* OFFSET parameter, or NULL if none */
+	ExprState  *limitCount;		/* COUNT parameter, or NULL if none */
 	long		offset;			/* current OFFSET value */
 	long		count;			/* current COUNT, if any */
 	bool		noCount;		/* if true, ignore count */
@@ -814,16 +991,4 @@ typedef struct LimitState
 	TupleTableSlot *subSlot;	/* tuple last obtained from subplan */
 } LimitState;
 
-/* ---------------------
- *	 SubPlanState information
- * ---------------------
- */
-typedef struct SubPlanState
-{
-	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 0c031b830e1..cc550131852 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.131 2002/12/12 20:35:14 tgl Exp $
+ * $Id: nodes.h,v 1.132 2002/12/13 19:46:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -31,12 +31,12 @@ typedef enum NodeTag
 	 * TAGS FOR EXECUTOR NODES (execnodes.h)
 	 */
 	T_IndexInfo = 10,
-	T_ResultRelInfo,
-	T_TupleTableSlot,
 	T_ExprContext,
 	T_ProjectionInfo,
 	T_JunkFilter,
+	T_ResultRelInfo,
 	T_EState,
+	T_TupleTableSlot,
 
 	/*
 	 * TAGS FOR PLAN NODES (plannodes.h)
@@ -89,7 +89,6 @@ typedef enum NodeTag
 	T_HashState,
 	T_SetOpState,
 	T_LimitState,
-	T_SubPlanState,
 
 	/*
 	 * TAGS FOR PRIMITIVE NODES (primnodes.h)
@@ -122,10 +121,27 @@ typedef enum NodeTag
 	T_JoinExpr,
 	T_FromExpr,
 
+	/*
+	 * TAGS FOR EXPRESSION STATE NODES (execnodes.h)
+	 *
+	 * These correspond (not always one-for-one) to primitive nodes
+	 * derived from Expr.
+	 */
+	T_ExprState = 400,
+	T_GenericExprState,
+	T_AggrefExprState,
+	T_ArrayRefExprState,
+	T_FuncExprState,
+	T_BoolExprState,
+	T_SubPlanExprState,
+	T_CaseExprState,
+	T_CaseWhenState,
+	T_ConstraintTestState,
+
 	/*
 	 * TAGS FOR PLANNER NODES (relation.h)
 	 */
-	T_RelOptInfo = 400,
+	T_RelOptInfo = 500,
 	T_IndexOptInfo,
 	T_Path,
 	T_IndexPath,
@@ -144,13 +160,13 @@ typedef enum NodeTag
 	/*
 	 * TAGS FOR MEMORY NODES (memnodes.h)
 	 */
-	T_MemoryContext = 500,
+	T_MemoryContext = 600,
 	T_AllocSetContext,
 
 	/*
 	 * TAGS FOR VALUE NODES (pg_list.h)
 	 */
-	T_Value = 600,
+	T_Value = 650,
 	T_List,
 	T_Integer,
 	T_Float,
diff --git a/src/include/nodes/params.h b/src/include/nodes/params.h
index 8f2eff5165e..c8bc2857ed8 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.19 2002/12/05 15:50:39 tgl Exp $
+ * $Id: params.h,v 1.20 2002/12/13 19:46:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -88,8 +88,8 @@ 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 SubPlanState node that needs to
- *	  be executed to produce the value.  (This is done so that we can have
+ *	  If execPlan is not NULL, it points to a SubPlanExprState 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
  *	  when needed.
@@ -98,7 +98,7 @@ typedef ParamListInfoData *ParamListInfo;
 
 typedef struct ParamExecData
 {
-	void	   *execPlan;		/* should be "SubPlanState *" */
+	void	   *execPlan;		/* should be "SubPlanExprState *" */
 	Datum		value;
 	bool		isnull;
 } ParamExecData;
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index ae0df7e441c..01a6dbf8d64 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: primnodes.h,v 1.73 2002/12/12 20:35:16 tgl Exp $
+ * $Id: primnodes.h,v 1.74 2002/12/13 19:46:00 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,9 +20,6 @@
 #include "access/attnum.h"
 #include "nodes/pg_list.h"
 
-/* FunctionCache is declared in utils/fcache.h */
-typedef struct FunctionCache *FunctionCachePtr;
-
 
 /* ----------------------------------------------------------------
  *						node definitions
@@ -221,9 +218,6 @@ typedef struct Aggref
 	Expr	   *target;			/* expression we are aggregating on */
 	bool		aggstar;		/* TRUE if argument was really '*' */
 	bool		aggdistinct;	/* TRUE if it's agg(DISTINCT ...) */
-
-	/* XXX this should move to AggrefExprState: */
-	int			aggno;			/* workspace for executor (see nodeAgg.c) */
 } Aggref;
 
 /* ----------------
@@ -307,8 +301,6 @@ typedef struct FuncExpr
 	bool		funcretset;		/* true if function returns set */
 	CoercionForm funcformat;	/* how to display this function call */
 	List	   *args;			/* arguments to the function */
-
-	FunctionCachePtr func_fcache;		/* XXX runtime state, or NULL */
 } FuncExpr;
 
 /*
@@ -328,8 +320,6 @@ typedef struct OpExpr
 	Oid			opresulttype;	/* PG_TYPE OID of result value */
 	bool		opretset;		/* true if operator returns set */
 	List	   *args;			/* arguments to the operator (1 or 2) */
-
-	FunctionCachePtr op_fcache; /* XXX runtime state, else NULL */
 } OpExpr;
 
 /*
@@ -463,8 +453,6 @@ typedef struct SubPlanExpr
 	SubLink    *sublink;		/* SubLink node from parser; holds info
 								 * about what to do with subselect's
 								 * results */
-
-	struct SubPlanState *pstate; /* XXX TEMPORARY HACK */
 } SubPlanExpr;
 
 /* ----------------
diff --git a/src/include/utils/fcache.h b/src/include/utils/fcache.h
deleted file mode 100644
index f043eb04284..00000000000
--- a/src/include/utils/fcache.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * fcache.h
- *		Declarations for function cache records.
- *
- * The first time any Oper or Func node is evaluated, we compute a cache
- * record for the function being invoked, and save a pointer to the cache
- * record in the Oper or Func node.  This saves repeated lookup of info
- * about the function.
- *
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- * $Id: fcache.h,v 1.23 2002/06/20 20:29:52 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#ifndef FCACHE_H
-#define FCACHE_H
-
-#include "fmgr.h"
-#include "nodes/execnodes.h"
-
-/*
- * A FunctionCache record is built for all functions regardless of language.
- *
- * We store the fmgr lookup info to avoid recomputing it on each call.
- *
- * We also need to store argument values across calls when evaluating a
- * function-returning-set.	This is pretty ugly (and not re-entrant);
- * current-evaluation info should be somewhere in the econtext, not in
- * the querytree.  As it stands, a function-returning-set can't safely be
- * recursive, at least not if it's in plpgsql which will try to re-use
- * the querytree at multiple execution nesting levels.	FIXME someday.
- */
-
-typedef struct FunctionCache
-{
-	/*
-	 * Function manager's lookup info for the target function.
-	 */
-	FmgrInfo	func;
-
-	/*
-	 * setArgsValid is true when we are evaluating a set-valued function
-	 * and we are in the middle of a call series; we want to pass the same
-	 * argument values to the function again (and again, until it returns
-	 * ExprEndResult).
-	 */
-	bool		setArgsValid;
-
-	/*
-	 * Flag to remember whether we found a set-valued argument to the
-	 * function. This causes the function result to be a set as well.
-	 * Valid only when setArgsValid is true.
-	 */
-	bool		setHasSetArg;	/* some argument returns a set */
-
-	/*
-	 * Current argument data for a set-valued function; contains valid
-	 * data only if setArgsValid is true.
-	 */
-	FunctionCallInfoData setArgs;
-} FunctionCache;
-
-
-extern FunctionCachePtr init_fcache(Oid foid, int nargs,
-			MemoryContext fcacheCxt);
-
-#endif   /* FCACHE_H */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 23d457263e8..8908a43a786 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.73 2002/12/12 15:49:42 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.74 2002/12/13 19:46:01 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -1981,6 +1981,7 @@ exec_prepare_plan(PLpgSQL_execstate * estate,
 	PLpgSQL_recfield *recfield;
 	int			i;
 	int			fno;
+	_SPI_plan  *spi_plan;
 	void	   *plan;
 	Oid		   *argtypes;
 
@@ -2030,7 +2031,8 @@ exec_prepare_plan(PLpgSQL_execstate * estate,
 	if (plan == NULL)
 		elog(ERROR, "SPI_prepare() failed on \"%s\"", expr->query);
 	expr->plan = SPI_saveplan(plan);
-	expr->plan_argtypes = ((_SPI_plan *) expr->plan)->argtypes;
+	spi_plan = (_SPI_plan *) expr->plan;
+	expr->plan_argtypes = spi_plan->argtypes;
 	expr->plan_simple_expr = NULL;
 	exec_simple_check_plan(expr);
 
@@ -3642,6 +3644,7 @@ exec_simple_check_plan(PLpgSQL_expr * expr)
 	_SPI_plan  *spi_plan = (_SPI_plan *) expr->plan;
 	Plan	   *plan;
 	TargetEntry *tle;
+	MemoryContext oldcontext;
 
 	expr->plan_simple_expr = NULL;
 
@@ -3688,10 +3691,13 @@ exec_simple_check_plan(PLpgSQL_expr * expr)
 		return;
 
 	/*
-	 * Yes - this is a simple expression. Remember the expression and the
-	 * return type
+	 * Yes - this is a simple expression.  Prepare to execute it, and
+	 * stash away the result type.  Put the expression state tree in the
+	 * plan context so it will have appropriate lifespan.
 	 */
-	expr->plan_simple_expr = (Node *) tle->expr;
+	oldcontext = MemoryContextSwitchTo(spi_plan->plancxt);
+	expr->plan_simple_expr = ExecInitExpr(tle->expr, NULL);
+	MemoryContextSwitchTo(oldcontext);
 	expr->plan_simple_type = exprType((Node *) tle->expr);
 }
 
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 2f4c10e0356..945569b6f6a 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -3,7 +3,7 @@
  *			  procedural language
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.29 2002/11/10 00:35:58 momjian Exp $
+ *	  $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.30 2002/12/13 19:46:01 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -165,7 +165,7 @@ typedef struct
 	int			exprno;
 	char	   *query;
 	void	   *plan;
-	Node	   *plan_simple_expr;
+	ExprState  *plan_simple_expr;
 	Oid			plan_simple_type;
 	Oid		   *plan_argtypes;
 	int			nparams;
-- 
GitLab