public inbox for [email protected]  
help / color / mirror / Atom feed
[PATCH 03/20] WIP: Faster expression processing and targetlist projection.
21+ messages / 2 participants
[nested] [flat]

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH 03/20] WIP: Faster expression processing and targetlist projection.
@ 2016-07-13 04:06 Andres Freund <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw)

---
 src/backend/bootstrap/bootstrap.c         |   2 +-
 src/backend/catalog/index.c               |  38 +-
 src/backend/catalog/toasting.c            |   2 +-
 src/backend/commands/analyze.c            |  10 +-
 src/backend/commands/indexcmds.c          |   4 +-
 src/backend/commands/tablecmds.c          |  14 +-
 src/backend/commands/trigger.c            |   8 +-
 src/backend/executor/Makefile             |   4 +-
 src/backend/executor/execExpr.c           | 915 ++++++++++++++++++++++++++++++
 src/backend/executor/execIndexing.c       |  21 +-
 src/backend/executor/execMain.c           |  17 +-
 src/backend/executor/execQual.c           | 414 +-------------
 src/backend/executor/execScan.c           |   4 +-
 src/backend/executor/execUtils.c          | 140 +----
 src/backend/executor/nodeAgg.c            |  14 +-
 src/backend/executor/nodeBitmapHeapscan.c |  18 +-
 src/backend/executor/nodeCtescan.c        |   7 +-
 src/backend/executor/nodeCustom.c         |   7 +-
 src/backend/executor/nodeForeignscan.c    |  16 +-
 src/backend/executor/nodeFunctionscan.c   |   7 +-
 src/backend/executor/nodeGather.c         |   7 +-
 src/backend/executor/nodeGroup.c          |  11 +-
 src/backend/executor/nodeHash.c           |  11 +-
 src/backend/executor/nodeHashjoin.c       |  47 +-
 src/backend/executor/nodeIndexonlyscan.c  |  16 +-
 src/backend/executor/nodeIndexscan.c      |  20 +-
 src/backend/executor/nodeMergejoin.c      |  32 +-
 src/backend/executor/nodeModifyTable.c    |  36 +-
 src/backend/executor/nodeNestloop.c       |  29 +-
 src/backend/executor/nodeResult.c         |  17 +-
 src/backend/executor/nodeSamplescan.c     |   7 +-
 src/backend/executor/nodeSeqscan.c        |   7 +-
 src/backend/executor/nodeSubplan.c        |  12 +-
 src/backend/executor/nodeSubqueryscan.c   |   7 +-
 src/backend/executor/nodeTidscan.c        |   7 +-
 src/backend/executor/nodeValuesscan.c     |   7 +-
 src/backend/executor/nodeWindowAgg.c      |   2 +-
 src/backend/executor/nodeWorktablescan.c  |   7 +-
 src/bin/initdb/initdb.c                   |   4 +-
 src/include/executor/executor.h           |  37 +-
 src/include/nodes/execnodes.h             | 177 ++++--
 src/include/nodes/nodes.h                 |   2 +
 42 files changed, 1353 insertions(+), 811 deletions(-)
 create mode 100644 src/backend/executor/execExpr.c

diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index e518e17..5b805b5 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1088,7 +1088,7 @@ 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 = NIL;
+	newind->il_info->ii_PredicateState = NULL;
 	/* no exclusion constraints at bootstrap time, so no need to copy */
 	Assert(indexInfo->ii_ExclusionOps == NULL);
 	Assert(indexInfo->ii_ExclusionProcs == NULL);
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 7b30e46..053a116 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index)
 
 	/* fetch index predicate if any */
 	ii->ii_Predicate = RelationGetIndexPredicate(index);
-	ii->ii_PredicateState = NIL;
+	ii->ii_PredicateState = NULL;
 
 	/* fetch exclusion constraint info if any */
 	if (indexStruct->indisexclusion)
@@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
 	double		reltuples;
-	List	   *predicate;
+	ExprState2  *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  In a normal index build, we use
@@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 		 * In a partial index, discard tuples that don't satisfy the
 		 * predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 
 	return reltuples;
 }
@@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Scan all live tuples in the base relation.
@@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation,
 		/*
 		 * In a partial index, ignore tuples that don't satisfy the predicate.
 		 */
-		if (predicate != NIL)
+		if (predicate != NULL)
 		{
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
@@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation,
 	HeapTuple	heapTuple;
 	Datum		values[INDEX_MAX_KEYS];
 	bool		isnull[INDEX_MAX_KEYS];
-	List	   *predicate;
+	ExprState2 *predicate;
 	TupleTableSlot *slot;
 	EState	   *estate;
 	ExprContext *econtext;
@@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation,
 	econtext->ecxt_scantuple = slot;
 
 	/* Set up execution state for predicate, if any. */
-	predicate = (List *)
-		ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-						estate);
+	predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 	/*
 	 * Prepare for scan of the base relation.  We need just those tuples
@@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation,
 			 * In a partial index, discard tuples that don't satisfy the
 			 * predicate.
 			 */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 
@@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation,
 
 	/* These may have been pointing to the now-gone estate */
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 }
 
 
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 564e10e..c618dc1 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index 5fcedd7..00c9d30 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		TupleTableSlot *slot;
 		EState	   *estate;
 		ExprContext *econtext;
-		List	   *predicate;
+		ExprState2 *predicate;
 		Datum	   *exprvals;
 		bool	   *exprnulls;
 		int			numindexrows,
@@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows,
 		econtext->ecxt_scantuple = slot;
 
 		/* Set up execution state for predicate. */
-		predicate = (List *)
-			ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-							estate);
+		predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 
 		/* Compute and save index expression values */
 		exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows,
 			ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
 
 			/* If index is partial, check predicate */
-			if (predicate != NIL)
+			if (predicate != NULL)
 			{
-				if (!ExecQual(predicate, econtext, false))
+				if (!ExecQual(predicate))
 					continue;
 			}
 			numindexrows++;
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index d14d540..508130e 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId,
 	indexInfo = makeNode(IndexInfo);
 	indexInfo->ii_Expressions = NIL;
 	indexInfo->ii_ExpressionsState = NIL;
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
@@ -549,7 +549,7 @@ DefineIndex(Oid relationId,
 	indexInfo->ii_Expressions = NIL;	/* for now */
 	indexInfo->ii_ExpressionsState = NIL;
 	indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
-	indexInfo->ii_PredicateState = NIL;
+	indexInfo->ii_PredicateState = NULL;
 	indexInfo->ii_ExclusionOps = NULL;
 	indexInfo->ii_ExclusionProcs = NULL;
 	indexInfo->ii_ExclusionStrats = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 86e9814..804ca92 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -179,7 +179,7 @@ typedef struct NewConstraint
 	Oid			refindid;		/* OID of PK's index, if FOREIGN */
 	Oid			conid;			/* OID of pg_constraint entry, if FOREIGN */
 	Node	   *qual;			/* Check expr or CONSTR_FOREIGN Constraint */
-	List	   *qualstate;		/* Execution state for CHECK */
+	ExprState2 *qualstate;		/* Execution state for CHECK */
 } NewConstraint;
 
 /*
@@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 		{
 			case CONSTR_CHECK:
 				needscan = true;
-				con->qualstate = (List *)
-					ExecPrepareExpr((Expr *) con->qual, estate);
+				con->qualstate = ExecPrepareQual((List *) con->qual, estate);
 				break;
 			case CONSTR_FOREIGN:
 				/* Nothing to do here */
@@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
 				switch (con->contype)
 				{
 					case CONSTR_CHECK:
-						if (!ExecQual(con->qualstate, econtext, true))
+						if (!ExecQual(con->qualstate))
 							ereport(ERROR,
 									(errcode(ERRCODE_CHECK_VIOLATION),
 									 errmsg("check constraint \"%s\" is violated by some row",
@@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	Datum		val;
 	char	   *conbin;
 	Expr	   *origexpr;
-	List	   *exprstate;
+	ExprState2 *exprstate;
 	TupleDesc	tupdesc;
 	HeapScanDesc scan;
 	HeapTuple	tuple;
@@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 			 HeapTupleGetOid(constrtup));
 	conbin = TextDatumGetCString(val);
 	origexpr = (Expr *) stringToNode(conbin);
-	exprstate = (List *)
-		ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+	exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
 
 	econtext = GetPerTupleExprContext(estate);
 	tupdesc = RelationGetDescr(rel);
@@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
 	{
 		ExecStoreTuple(tuple, slot, InvalidBuffer, false);
 
-		if (!ExecQual(exprstate, econtext, true))
+		if (!ExecQual(exprstate))
 			ereport(ERROR,
 					(errcode(ERRCODE_CHECK_VIOLATION),
 					 errmsg("check constraint \"%s\" is violated by some row",
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 99a659a..0929652 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 	if (trigger->tgqual)
 	{
 		TupleDesc	tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
-		List	  **predicate;
+		ExprState2 **predicate;
 		ExprContext *econtext;
 		TupleTableSlot *oldslot = NULL;
 		TupleTableSlot *newslot = NULL;
@@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 * nodetrees for it.  Keep them in the per-query memory context so
 		 * they'll survive throughout the query.
 		 */
-		if (*predicate == NIL)
+		if (*predicate == NULL)
 		{
 			Node	   *tgqual;
 
@@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 			ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
 			/* ExecQual wants implicit-AND form */
 			tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
-			*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+			*predicate = ExecPrepareQual((List *) tgqual, estate);
 			MemoryContextSwitchTo(oldContext);
 		}
 
@@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
 		 */
 		econtext->ecxt_innertuple = oldslot;
 		econtext->ecxt_outertuple = newslot;
-		if (!ExecQual(*predicate, econtext, false))
+		if (!ExecQual(*predicate))
 			return false;
 	}
 
diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile
index 51edd4c..ba7eb50 100644
--- a/src/backend/executor/Makefile
+++ b/src/backend/executor/Makefile
@@ -12,8 +12,8 @@ subdir = src/backend/executor
 top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
-       execMain.o execParallel.o execProcnode.o execQual.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \
+       execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \
        execScan.o execTuples.o \
        execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
        nodeBitmapAnd.o nodeBitmapOr.o \
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
new file mode 100644
index 0000000..fe5575f
--- /dev/null
+++ b/src/backend/executor/execExpr.c
@@ -0,0 +1,915 @@
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "access/tupconvert.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execdebug.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
+#include "parser/parse_coerce.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/acl.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/memutils.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull);
+
+static void
+ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s)
+{
+	if (es->steps_alloc == 0)
+	{
+		es->steps_alloc = 16;
+		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+	else if (es->steps_alloc == es->steps_len)
+	{
+		es->steps_alloc *= 2;
+		es->steps = repalloc(es->steps,
+							 sizeof(ExprEvalStep) * es->steps_alloc);
+	}
+
+	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static TupleTableSlot** varslot(Var *variable, ExprContext *econtext)
+{
+	TupleTableSlot **slot;
+
+	switch (variable->varno)
+	{
+		case INNER_VAR: /* get the tuple from the inner node */
+			slot = &econtext->ecxt_innertuple;
+			break;
+
+		case OUTER_VAR: /* get the tuple from the outer node */
+			slot = &econtext->ecxt_outertuple;
+			break;
+
+			/* INDEX_VAR is handled by default case */
+
+		default:				/* get the tuple from the relation being
+								 * scanned */
+			slot = &econtext->ecxt_scantuple;
+			break;
+	}
+
+	return slot;
+}
+
+static void
+ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+	ExprState *es = ExecInitExpr(expr, parent);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	scratch.opcode = EEO_EVAL1;
+	scratch.d.eval1.expr = es;
+	ExprEvalPushStep(state, &scratch);
+}
+
+static bool
+ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid,
+			  PlanState *parent, ExprContext *econtext, ExprState2 *state,  Datum *resv, bool *resnull)
+{
+	ListCell   *lc;
+	AclResult	aclresult;
+	int nargs = list_length(args);
+	FunctionCallInfo fcinfo;
+	int argno;
+
+	/* Check permission to call function */
+	aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+	if (aclresult != ACLCHECK_OK)
+		aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+	InvokeFunctionExecuteHook(funcid);
+
+	/*
+	 * Safety check on nargs.  Under normal circumstances this should never
+	 * fail, as parser should check sooner.  But possibly it might fail if
+	 * server has been compiled with FUNC_MAX_ARGS smaller than some functions
+	 * declared in pg_proc?
+	 */
+	if (nargs > FUNC_MAX_ARGS)
+		ereport(ERROR,
+				(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+				 errmsg_plural("cannot pass more than %d argument to a function",
+							   "cannot pass more than %d arguments to a function",
+							   FUNC_MAX_ARGS,
+							   FUNC_MAX_ARGS)));
+
+	/* Set up the primary fmgr lookup information */
+	scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+	scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+	fcinfo = scratch->d.func.fcinfo_data;
+	fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory);
+	fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+	InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+							 nargs, inputcollid, NULL, NULL);
+	scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+	if (scratch->d.func.finfo->fn_retset)
+	{
+		elog(ERROR, "targetlist SRFs are not supported");
+		state->failed = true;
+		return false;
+	}
+
+	argno = 0;
+	foreach (lc, args)
+	{
+		Expr *arg = (Expr *) lfirst(lc);
+
+		if (IsA(arg, Const))
+		{
+			/*
+			 * Don't evaluate const arguments every round; especially
+			 * interesting for constants in comparisons.
+			 */
+			Const *con = (Const *) arg;
+
+			fcinfo->arg[argno] = con->constvalue;
+			fcinfo->argnull[argno] = con->constisnull;
+		}
+		else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]))
+		{
+			state->failed = true;
+			return false;
+		}
+		argno++;
+	}
+
+	scratch->d.func.nargs = nargs;
+	/*
+	 * FIXME: introduce separate opcodes for functioncallusage
+	 * enabled/disabled.
+	 */
+	if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT;
+		else
+			scratch->opcode = EEO_FUNCEXPR;
+	}
+	else
+	{
+		if (scratch->d.func.finfo->fn_strict)
+			scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+		else
+			scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+	}
+	return true;
+}
+
+static void
+ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node)
+{
+	ExprEvalStep scratch;
+	int last_outer = -1;
+	int last_inner = -1;
+	int last_scan = -1;
+
+	/*
+	 * Figure out which attributes we're going to need.
+	 */
+	ExecGetLastAttnums((Node *) node,
+					   &last_outer,
+					   &last_inner,
+					   &last_scan);
+	if (last_outer > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_outertuple;
+		scratch.d.fetch.last_var = last_outer;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_inner > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_innertuple;
+		scratch.d.fetch.last_var = last_inner;
+		ExprEvalPushStep(state, &scratch);
+	}
+	if (last_scan > 0)
+	{
+		scratch.opcode = EEO_FETCHSOMEATTRS;
+		scratch.d.fetch.slot = &econtext->ecxt_scantuple;
+		scratch.d.fetch.last_var = last_scan;
+		ExprEvalPushStep(state, &scratch);
+	}
+}
+
+ExprState2 *
+ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	if (list_length(qual) > 1)
+	{
+		andExpr = makeNode(BoolExpr);
+
+		/* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */
+		andExpr->boolop = AND_EXPR;
+		andExpr->args = qual;
+		andExpr->location = -1;
+		return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+	}
+	else
+	{
+		return ExecInitExpr2((Expr *) linitial(qual), parent, econtext);
+	}
+}
+
+
+ExprState2 *
+ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext)
+{
+	BoolExpr *andExpr;
+
+	if (qual == NULL)
+		return NULL;
+
+	Assert(IsA(qual, List));
+
+	andExpr = makeNode(BoolExpr);
+
+	andExpr->boolop = AND_EXPR;
+	andExpr->args = qual;
+	andExpr->location = -1;
+
+	return ExecInitExpr2((Expr *) andExpr, parent, econtext);
+}
+
+/* ----------------
+ *		ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot.  (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!)  Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor.  It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made.  At higher levels of a plan,
+ * there is no need to recheck.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+						ExprContext *econtext,
+						TupleTableSlot *slot,
+						PlanState *parent,
+						TupleDesc inputDesc)
+{
+	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+	ExprEvalStep scratch;
+	ListCell *lc;
+	ExprState2 *state;
+
+	projInfo->pi_slot = slot;
+	projInfo->pi_exprContext = econtext;
+	projInfo->pi_state.econtext = econtext;
+	projInfo->pi_state.tag.type = T_ExprState2;
+	state = &projInfo->pi_state;
+	ExecInitExprSlots(state, econtext, (Node *) targetList);
+
+	foreach(lc, targetList)
+	{
+		TargetEntry *tle;
+		Var		   *variable = NULL;
+		AttrNumber	attnum;
+		bool		isSimpleVar = false;
+
+		Assert(IsA(lfirst(lc), TargetEntry));
+
+		tle = (TargetEntry *) lfirst(lc);
+
+		if (tle->expr != NULL &&
+			IsA(tle, Var) &&
+			((Var *)tle->expr)->varattno > 0)
+		{
+			variable = (Var *) tle->expr;
+			attnum = variable->varattno;
+
+			if (!inputDesc)
+				isSimpleVar = true;		/* can't check type, assume OK */
+			else if (variable->varattno <= inputDesc->natts)
+			{
+				Form_pg_attribute attr;
+
+				attr = inputDesc->attrs[variable->varattno - 1];
+				if (!attr->attisdropped && variable->vartype == attr->atttypid)
+					isSimpleVar = true;
+			}
+		}
+
+		if (isSimpleVar)
+		{
+			scratch.opcode = EEO_ASSIGN_VAR;
+			scratch.d.assign_var.slot = varslot(variable, econtext);
+			scratch.d.assign_var.attnum = attnum - 1;
+			scratch.d.assign_var.resultnum = tle->resno - 1;
+			ExprEvalPushStep(state, &scratch);
+		}
+		else
+		{
+			if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull))
+			{
+				elog(ERROR, "could not compute target list");
+			}
+			else
+			{
+				scratch.opcode = EEO_ASSIGN_TMP;
+				scratch.d.assign_tmp.resultnum = tle->resno - 1;
+				ExprEvalPushStep(state, &scratch);
+			}
+		}
+	}
+
+
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return projInfo;
+}
+
+
+ExprState2 *
+ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext)
+{
+	ExprState2 *state = makeNode(ExprState2);
+	ExprEvalStep scratch;
+
+	if (node == NULL)
+		return NULL;
+
+	state->econtext = econtext;
+
+	ExecInitExprSlots(state, econtext, (Node *) node);
+
+	if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull))
+		return NULL;
+	scratch.resvalue = &state->resvalue;
+	scratch.resnull = &state->resnull;
+	scratch.opcode = EEO_DONE;
+	ExprEvalPushStep(state, &scratch);
+	return state;
+}
+
+static bool
+ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull)
+{
+	ExprEvalStep scratch;
+
+	Assert(resv != NULL && resnull != NULL);
+	scratch.resvalue = resv;
+	scratch.resnull = resnull;
+	Assert(econtext != NULL);
+
+	switch (nodeTag(node))
+	{
+		/* suppported */
+		case T_Var:
+			/* varattno == InvalidAttrNumber means it's a whole-row Var */
+			if (((Var *) node)->varattno == InvalidAttrNumber)
+			{
+				ExecInitOld(state, parent, node, resv, resnull);
+			}
+			else
+			{
+				Var *variable = (Var *) node;
+				TupleTableSlot **slot = varslot(variable, econtext);
+
+				/* FIXME: consider re-adding type checks */
+				if (variable->varattno <= 0)
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno;
+					scratch.opcode = EEO_SYSVAR;
+				}
+				else
+				{
+					scratch.d.var.slot = slot;
+					scratch.d.var.attnum = variable->varattno - 1;
+					scratch.opcode = EEO_VAR;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_Const:
+			{
+				Const *con = (Const *) node;
+
+				/* FIXME: skip evaluation */
+				scratch.opcode = EEO_CONST;
+				scratch.d.constval.value = con->constvalue;
+				scratch.d.constval.isnull = con->constisnull;
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_FuncExpr:
+			{
+				FuncExpr   *func = (FuncExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_OpExpr:
+			{
+				OpExpr   *op = (OpExpr *) node;
+				if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+								   parent, econtext, state, resv, resnull))
+				{
+					Assert(false);
+					return false;
+				}
+				ExprEvalPushStep(state, &scratch);
+			}
+			break;
+		case T_BoolExpr:
+			{
+				BoolExpr   *boolexpr = (BoolExpr *) node;
+				ListCell   *lc;
+				List *adjust_bailout = NIL;
+				bool first = true;
+
+				scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+				scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+				scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+				foreach (lc, boolexpr->args)
+				{
+					Expr *arg = (Expr *) lfirst(lc);
+
+					switch (boolexpr->boolop)
+					{
+						case AND_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_AND_STEP;
+							break;
+						case OR_EXPR:
+							if (first)
+								scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+							else
+								scratch.opcode = EEO_BOOL_OR_STEP;
+							break;
+						case NOT_EXPR:
+							Assert(list_length(boolexpr->args) == 1);
+							scratch.opcode = EEO_BOOL_NOT_STEP;
+							break;
+						default:
+							elog(ERROR, "unrecognized boolop: %d",
+								 (int) boolexpr->boolop);
+							break;
+					}
+
+					first = false;
+
+					if (!ExecInitExprRec(arg, parent, econtext, state,
+										 scratch.d.boolexpr.value,
+										 scratch.d.boolexpr.isnull))
+						return false;
+					scratch.d.boolexpr.jumpdone = -1;
+					ExprEvalPushStep(state, &scratch);
+					adjust_bailout = lappend_int(adjust_bailout,
+												 state->steps_len - 1);
+				}
+
+				/* adjust early bail out jump target */
+				foreach (lc, adjust_bailout)
+				{
+					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+					Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST &&
+						   as->opcode <= EEO_BOOL_NOT_STEP);
+					Assert(as->d.boolexpr.jumpdone == -1);
+					as->d.boolexpr.jumpdone = state->steps_len;
+				}
+			}
+			break;
+		/* unsupported */
+		case T_Param:
+		case T_CoerceToDomainValue:
+		case T_CaseTestExpr:
+		case T_Aggref:
+		case T_GroupingFunc:
+		case T_WindowFunc:
+		case T_ArrayRef:
+		case T_DistinctExpr:
+		case T_NullIfExpr:
+		case T_ScalarArrayOpExpr:
+		case T_SubPlan:
+		case T_AlternativeSubPlan:
+		case T_FieldSelect:
+		case T_FieldStore:
+		case T_RelabelType:
+		case T_CoerceViaIO:
+		case T_ArrayCoerceExpr:
+		case T_ConvertRowtypeExpr:
+		case T_CaseExpr:
+		case T_ArrayExpr:
+		case T_RowExpr:
+		case T_RowCompareExpr:
+		case T_CoalesceExpr:
+		case T_MinMaxExpr:
+		case T_XmlExpr:
+		case T_NullTest:
+		case T_BooleanTest:
+		case T_CoerceToDomain:
+		case T_CurrentOfExpr:
+			ExecInitOld(state, parent, node, resv, resnull);
+			break;
+		case T_TargetEntry:
+		case T_List:
+			elog(ERROR, "unexpected");
+		default:
+			elog(ERROR, "unrecognized node type: %d",
+				 (int) nodeTag(node));
+			break;
+	}
+	return true;
+}
+
+/*
+ * ExecProjectIntoSlot
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+void
+ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot)
+{
+	bool isnull;
+	MemoryContext oldcontext;
+	ExprContext *econtext;
+	ExprState2 *state;
+
+	state = &projInfo->pi_state;
+	econtext = state->econtext;
+
+	/* make conditional? */
+	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+
+	/*
+	 * Clear any former contents of the result slot.  This makes it safe for
+	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
+	 * return the slot as-is if we decide no rows can be projected.)
+	 */
+	ExecClearTuple(slot);
+
+	state->resultslot = slot;
+	ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldcontext);
+
+	/*
+	 * Successfully formed a result row.  Mark the result slot as containing a
+	 * valid virtual tuple.
+	 */
+	ExecStoreVirtualTuple(slot);
+}
+
+bool
+ExecQual(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	/* error out? */
+	if (state == NULL)
+		return true;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return false;
+	return DatumGetBool(ret);
+}
+
+
+bool
+ExecCheck(ExprState2 *state)
+{
+	MemoryContext oldContext;
+	bool isnull;
+	Datum ret;
+
+	oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory);
+
+	ret = ExecEvalExpr2(state, &isnull);
+
+	MemoryContextSwitchTo(oldContext);
+
+	if (isnull)
+		return true;
+	return DatumGetBool(ret);
+}
+
+/* FIXME: use computed goto only on gcc / clang */
+#define USE_COMPUTED_GOTO
+
+#ifdef USE_COMPUTED_GOTO
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode];
+#define EEO_CASE(name) CASE_##name
+#else
+#define EEO_SWITCH(d) switch (d)
+#define EEO_DISPATCH(op) goto starteval
+#define EEO_CASE(name) case name
+#endif /* USE_COMPUTED_GOTO */
+
+Datum
+ExecEvalExpr2(ExprState2 *state, bool *isnull)
+{
+	ExprEvalStep *op = state->steps;
+	TupleTableSlot *resultslot = state->resultslot;
+
+#ifdef USE_COMPUTED_GOTO
+	static const void **dispatch_table[] = {
+		&&CASE_EEO_VAR,
+		&&CASE_EEO_SYSVAR,
+		&&CASE_EEO_CONST,
+		&&CASE_EEO_FUNCEXPR,
+		&&CASE_EEO_FUNCEXPR_STRICT,
+		&&CASE_EEO_FUNCEXPR_FUSAGE,
+		&&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+		&&CASE_EEO_BOOL_AND_STEP_FIRST,
+		&&CASE_EEO_BOOL_AND_STEP,
+		&&CASE_EEO_BOOL_OR_STEP_FIRST,
+		&&CASE_EEO_BOOL_OR_STEP,
+		&&CASE_EEO_BOOL_NOT_STEP,
+		&&CASE_EEO_FETCHSOMEATTRS,
+		&&CASE_EEO_ASSIGN_VAR,
+		&&CASE_EEO_ASSIGN_TMP,
+		&&CASE_EEO_EVAL1,
+		&&CASE_EEO_ASSIGN_EVAL1,
+		&&CASE_EEO_DONE
+	};
+#endif
+
+#ifdef USE_COMPUTED_GOTO
+	EEO_DISPATCH(op);
+#else
+starteval:
+#endif
+	EEO_SWITCH (op->opcode)
+	{
+		EEO_CASE(EEO_VAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				/*
+				 * Yuck): can't assert tts_nvalid, wholrow vars or such
+				 * could have materialized the slot - but the contents are
+				 * still valid :/
+				 *
+				 * Assert(op->d.var.attnum <= slot->tts_nvalid);
+				 */
+				Assert(op->d.var.attnum >= 0);
+				*op->resnull = slot->tts_isnull[attnum];
+				*op->resvalue = slot->tts_values[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_SYSVAR):
+			{
+				TupleTableSlot *slot = *op->d.var.slot;
+				int attnum = op->d.var.attnum;
+				Assert(op->d.var.attnum < 0);
+				*op->resvalue = heap_getsysattr(slot->tts_tuple, attnum,
+												slot->tts_tupleDescriptor,
+												op->resnull);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_CONST):
+			*op->resnull = op->d.constval.isnull;
+			*op->resvalue = op->d.constval.value;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT):
+			{
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail;
+					}
+				}
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail:
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE):
+			{
+				PgStat_FunctionCallUsage fcusage;
+				FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+				int argno;
+
+				/* strict function, check for NULL args */
+				for (argno = 0; argno < op->d.func.nargs;argno++)
+				{
+					if (fcinfo->argnull[argno])
+					{
+						*op->resnull = true;
+						goto strictfail_fusage;
+					}
+				}
+
+				pgstat_init_function_usage(fcinfo, &fcusage);
+
+				fcinfo->isnull = false;
+				*op->resvalue = (op->d.func.fn_addr)(fcinfo);
+				*op->resnull = fcinfo->isnull;
+		strictfail_fusage:
+				pgstat_end_function_usage(&fcusage, true);
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_AND_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_AND_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+			else if (!DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(false);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_OR_STEP_FIRST):
+			*op->d.boolexpr.anynull = false;
+		EEO_CASE(EEO_BOOL_OR_STEP):
+			*op->resvalue = *op->d.boolexpr.value;
+			*op->resnull = *op->d.boolexpr.isnull;
+
+			if (*op->d.boolexpr.isnull)
+			{
+				*op->d.boolexpr.anynull = true;
+			}
+
+			if (DatumGetBool(*op->d.boolexpr.value))
+			{
+				*op->resnull = false;
+				*op->resvalue = BoolGetDatum(true);
+				/* bail out early */
+				op = &state->steps[op->d.boolexpr.jumpdone];
+				EEO_DISPATCH(op);
+			}
+
+			/* XXX: could skip this step till last */
+			if (*op->d.boolexpr.anynull)
+			{
+				*op->resnull = *op->d.boolexpr.anynull;
+				*op->resvalue = BoolGetDatum(false); /* or null */
+			}
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_BOOL_NOT_STEP):
+			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+			*op->resnull = *op->d.boolexpr.isnull;
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_FETCHSOMEATTRS):
+			/* XXX: add check for tts_nvalid here? */
+			slot_getsomeattrs(*op->d.fetch.slot,
+							  op->d.fetch.last_var);
+			op++;
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_VAR):
+			{
+				size_t resultnum = op->d.assign_var.resultnum;
+				size_t attnum = op->d.assign_var.attnum;
+				TupleTableSlot *source = *op->d.assign_var.slot;
+				resultslot->tts_values[resultnum] = source->tts_values[attnum];
+				resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum];
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_ASSIGN_TMP):
+			{
+				size_t resultnum = op->d.assign_tmp.resultnum;
+				resultslot->tts_values[resultnum] = state->resvalue;
+				resultslot->tts_isnull[resultnum] = state->resnull;
+				op++;
+			}
+			EEO_DISPATCH(op);
+		EEO_CASE(EEO_EVAL1):
+			{
+				*op->resvalue = ExecEvalExpr(op->d.eval1.expr,
+											 state->econtext,
+											 op->resnull,
+											 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_ASSIGN_EVAL1):
+			{
+				size_t resultnum = op->d.assign_eval1.resultnum;
+				resultslot->tts_values[resultnum] =
+					ExecEvalExpr(op->d.assign_eval1.expr,
+								 state->econtext,
+								 &resultslot->tts_isnull[resultnum],
+								 NULL);
+				op++;
+				EEO_DISPATCH(op);
+			}
+		EEO_CASE(EEO_DONE):
+			goto out;
+	}
+out:
+	*isnull = state->resnull;
+	return state->resvalue;
+}
+
+#undef EEO_SWITCH
+#undef EEO_DISPATCH
+#undef EEO_CASE
diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c
index 0e2d834..8e1cbba 100644
--- a/src/backend/executor/execIndexing.c
+++ b/src/backend/executor/execIndexing.c
@@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
@@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
 		/* Check for partial index */
 		if (indexInfo->ii_Predicate != NIL)
 		{
-			List	   *predicate;
+			ExprState2 *predicate;
 
 			/*
 			 * If predicate state not set up yet, create it (in the estate's
 			 * per-query context)
 			 */
 			predicate = indexInfo->ii_PredicateState;
-			if (predicate == NIL)
+			if (predicate == NULL)
 			{
-				predicate = (List *)
-					ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
-									estate);
+				predicate =
+					ExecPrepareQual(indexInfo->ii_Predicate, estate);
 				indexInfo->ii_PredicateState = predicate;
 			}
 
 			/* Skip this index-update if the predicate isn't satisfied */
-			if (!ExecQual(predicate, econtext, false))
+			if (!ExecQual(predicate))
 				continue;
 		}
 
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..66003c9 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
 
 		resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
 			palloc0(n * sizeof(FmgrInfo));
-		resultRelInfo->ri_TrigWhenExprs = (List **)
+		resultRelInfo->ri_TrigWhenExprs = (ExprState2 **)
 			palloc0(n * sizeof(List *));
 		if (instrument_options)
 			resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
@@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	ConstrCheck *check = rel->rd_att->constr->check;
 	ExprContext *econtext;
 	MemoryContext oldContext;
-	List	   *qual;
 	int			i;
 
 	/*
@@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	{
 		oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
 		resultRelInfo->ri_ConstraintExprs =
-			(List **) palloc(ncheck * sizeof(List *));
+			(ExprState2 **) palloc(ncheck * sizeof(ExprState2 *));
 		for (i = 0; i < ncheck; i++)
 		{
+			List *qual;
 			/* ExecQual wants implicit-AND form */
 			qual = make_ands_implicit(stringToNode(check[i].ccbin));
-			resultRelInfo->ri_ConstraintExprs[i] = (List *)
-				ExecPrepareExpr((Expr *) qual, estate);
+			resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate);
 		}
 		MemoryContextSwitchTo(oldContext);
 	}
@@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
 	/* And evaluate the constraints */
 	for (i = 0; i < ncheck; i++)
 	{
-		qual = resultRelInfo->ri_ConstraintExprs[i];
+		ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i];
 
 		/*
 		 * NOTE: SQL specifies that a NULL result from a constraint expression
 		 * is not to be treated as a failure.  Therefore, tell ExecQual to
 		 * return TRUE for NULL.
 		 */
-		if (!ExecQual(qual, econtext, true))
+		if (!ExecCheck(qual))
 			return check[i].ccname;
 	}
 
@@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 			l2, resultRelInfo->ri_WithCheckOptionExprs)
 	{
 		WithCheckOption *wco = (WithCheckOption *) lfirst(l1);
-		ExprState  *wcoExpr = (ExprState *) lfirst(l2);
+		ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2);
 
 		/*
 		 * Skip any WCOs which are not the kind we are looking for at this
@@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
 		 * need ExecQual to return FALSE for NULL to handle the view case (the
 		 * opposite of what we do above for CHECK constraints).
 		 */
-		if (!ExecQual((List *) wcoExpr, econtext, false))
+		if (!ExecQual(wcoExpr))
 		{
 			char	   *val_desc;
 			Bitmapset  *modifiedCols;
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index d04d1a8..fb070e4 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate)
 }
 
 
+/*
+ * ExecPrepareQual --- initialize for qual execution outside a normal
+ * Plan tree context.
+ *
+ * This differs from ExecInitExpr in that we don't assume the caller is
+ * already running in the EState's per-query context.  Also, we run the
+ * passed expression tree through expression_planner() to prepare it for
+ * execution.  (In ordinary Plan trees the regular planning process will have
+ * made the appropriate transformations on expressions, but for standalone
+ * expressions this won't have happened.)
+ */
+ExprState2 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+	ExprState2  *result;
+	MemoryContext oldcontext;
+
+	oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+	qual = (List *) expression_planner((Expr *) qual);
+
+	result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate));
+
+	MemoryContextSwitchTo(oldcontext);
+
+	return result;
+}
+
+
 /* ----------------------------------------------------------------
  *					 ExecQual / ExecTargetList / ExecProject
  * ----------------------------------------------------------------
  */
 
-/* ----------------------------------------------------------------
- *		ExecQual
- *
- *		Evaluates a conjunctive boolean expression (qual list) and
- *		returns true iff none of the subexpressions are false.
- *		(We also return true if the list is empty.)
- *
- *	If some of the subexpressions yield NULL but none yield FALSE,
- *	then the result of the conjunction is NULL (ie, unknown)
- *	according to three-valued boolean logic.  In this case,
- *	we return the value specified by the "resultForNull" parameter.
- *
- *	Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- *	since SQL specifies that tuples with null WHERE results do not
- *	get selected.  On the other hand, callers evaluating constraint
- *	conditions should pass resultForNull=TRUE, since SQL also specifies
- *	that NULL constraint conditions are not failures.
- *
- *	NOTE: it would not be correct to use this routine to evaluate an
- *	AND subclause of a boolean expression; for that purpose, a NULL
- *	result must be returned as NULL so that it can be properly treated
- *	in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- *	This routine is only used in contexts where a complete expression
- *	is being evaluated and we know that NULL can be treated the same
- *	as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
-	bool		result;
-	MemoryContext oldContext;
-	ListCell   *l;
-
-	/*
-	 * debugging stuff
-	 */
-	EV_printf("ExecQual: qual is ");
-	EV_nodeDisplay(qual);
-	EV_printf("\n");
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * Evaluate the qual conditions one at a time.  If we find a FALSE result,
-	 * we can stop evaluating and return FALSE --- the AND result must be
-	 * FALSE.  Also, if we find a NULL result when resultForNull is FALSE, we
-	 * can stop and return FALSE --- the AND result must be FALSE or NULL in
-	 * that case, and the caller doesn't care which.
-	 *
-	 * If we get to the end of the list, we can return TRUE.  This will happen
-	 * when the AND result is indeed TRUE, or when the AND result is NULL (one
-	 * or more NULL subresult, with all the rest TRUE) and the caller has
-	 * specified resultForNull = TRUE.
-	 */
-	result = true;
-
-	foreach(l, qual)
-	{
-		ExprState  *clause = (ExprState *) lfirst(l);
-		Datum		expr_value;
-		bool		isNull;
-
-		expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL);
-
-		if (isNull)
-		{
-			if (resultForNull == false)
-			{
-				result = false; /* treat NULL as FALSE */
-				break;
-			}
-		}
-		else
-		{
-			if (!DatumGetBool(expr_value))
-			{
-				result = false; /* definitely FALSE */
-				break;
-			}
-		}
-	}
-
-	MemoryContextSwitchTo(oldContext);
-
-	return result;
-}
-
 /*
  * Number of items in a tlist (including any resjunk items!)
  */
@@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist)
 	}
 	return len;
 }
-
-/*
- * ExecTargetList
- *		Evaluates a targetlist with respect to the given
- *		expression context.  Returns TRUE if we were able to create
- *		a result, FALSE if we have exhausted a set-valued expression.
- *
- * Results are stored into the passed values and isnull arrays.
- * The caller must provide an itemIsDone array that persists across calls.
- *
- * As with ExecEvalExpr, the caller should pass isDone = NULL if not
- * prepared to deal with sets of result tuples.  Otherwise, a return
- * of *isDone = ExprMultipleResult signifies a set element, and a return
- * of *isDone = ExprEndResult signifies end of the set of tuple.
- * We assume that *isDone has been initialized to ExprSingleResult by caller.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status.  It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static bool
-ExecTargetList(List *targetlist,
-			   TupleDesc tupdesc,
-			   ExprContext *econtext,
-			   Datum *values,
-			   bool *isnull,
-			   ExprDoneCond *itemIsDone,
-			   ExprDoneCond *isDone)
-{
-	Form_pg_attribute *att = tupdesc->attrs;
-	MemoryContext oldContext;
-	ListCell   *tl;
-	bool		haveDoneSets;
-
-	/*
-	 * Run in short-lived per-tuple context while computing expressions.
-	 */
-	oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
-	/*
-	 * evaluate all the expressions in the target list
-	 */
-	haveDoneSets = false;		/* any exhausted set exprs in tlist? */
-
-	foreach(tl, targetlist)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-		AttrNumber	resind = tle->resno - 1;
-
-		values[resind] = ExecEvalExpr(gstate->arg,
-									  econtext,
-									  &isnull[resind],
-									  &itemIsDone[resind]);
-
-		values[resind] = MakeExpandedObjectReadOnly(values[resind],
-													isnull[resind],
-													att[resind]->attlen);
-
-		if (itemIsDone[resind] != ExprSingleResult)
-		{
-			/* We have a set-valued expression in the tlist */
-			if (isDone == NULL)
-				ereport(ERROR,
-						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-						 errmsg("set-valued function called in context that cannot accept a set")));
-			if (itemIsDone[resind] == ExprMultipleResult)
-			{
-				/* we have undone sets in the tlist, set flag */
-				*isDone = ExprMultipleResult;
-			}
-			else
-			{
-				/* we have done sets in the tlist, set flag for that */
-				haveDoneSets = true;
-			}
-		}
-	}
-
-	if (haveDoneSets)
-	{
-		/*
-		 * note: can't get here unless we verified isDone != NULL
-		 */
-		if (*isDone == ExprSingleResult)
-		{
-			/*
-			 * all sets are done, so report that tlist expansion is complete.
-			 */
-			*isDone = ExprEndResult;
-			MemoryContextSwitchTo(oldContext);
-			return false;
-		}
-		else
-		{
-			/*
-			 * We have some done and some undone sets.  Restart the done ones
-			 * so that we can deliver a tuple (if possible).
-			 */
-			foreach(tl, targetlist)
-			{
-				GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-				TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-				AttrNumber	resind = tle->resno - 1;
-
-				if (itemIsDone[resind] == ExprEndResult)
-				{
-					values[resind] = ExecEvalExpr(gstate->arg,
-												  econtext,
-												  &isnull[resind],
-												  &itemIsDone[resind]);
-
-					values[resind] = MakeExpandedObjectReadOnly(values[resind],
-															  isnull[resind],
-														att[resind]->attlen);
-
-					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;
-					}
-				}
-			}
-
-			/*
-			 * 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)
-				{
-					GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-					TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-					AttrNumber	resind = tle->resno - 1;
-
-					while (itemIsDone[resind] == ExprMultipleResult)
-					{
-						values[resind] = ExecEvalExpr(gstate->arg,
-													  econtext,
-													  &isnull[resind],
-													  &itemIsDone[resind]);
-						/* no need for MakeExpandedObjectReadOnly */
-					}
-				}
-
-				MemoryContextSwitchTo(oldContext);
-				return false;
-			}
-		}
-	}
-
-	/* Report success */
-	MemoryContextSwitchTo(oldContext);
-
-	return true;
-}
-
-/*
- * ExecProject
- *
- *		projects a tuple based on projection info and stores
- *		it in the previously specified tuple table slot.
- *
- *		Note: the result is always a virtual tuple; therefore it
- *		may reference the contents of the exprContext's scan tuples
- *		and/or temporary results constructed in the exprContext.
- *		If the caller wishes the result to be valid longer than that
- *		data will be valid, he must call ExecMaterializeSlot on the
- *		result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
-{
-	TupleTableSlot *slot;
-	ExprContext *econtext;
-	int			numSimpleVars;
-
-	/*
-	 * sanity checks
-	 */
-	Assert(projInfo != NULL);
-
-	/*
-	 * get the projection info we want
-	 */
-	slot = projInfo->pi_slot;
-	econtext = projInfo->pi_exprContext;
-
-	/* Assume single result row until proven otherwise */
-	if (isDone)
-		*isDone = ExprSingleResult;
-
-	/*
-	 * Clear any former contents of the result slot.  This makes it safe for
-	 * us to use the slot's Datum/isnull arrays as workspace. (Also, we can
-	 * return the slot as-is if we decide no rows can be projected.)
-	 */
-	ExecClearTuple(slot);
-
-	/*
-	 * Force extraction of all input values that we'll need.  The
-	 * Var-extraction loops below depend on this, and we are also prefetching
-	 * all attributes that will be referenced in the generic expressions.
-	 */
-	if (projInfo->pi_lastInnerVar > 0)
-		slot_getsomeattrs(econtext->ecxt_innertuple,
-						  projInfo->pi_lastInnerVar);
-	if (projInfo->pi_lastOuterVar > 0)
-		slot_getsomeattrs(econtext->ecxt_outertuple,
-						  projInfo->pi_lastOuterVar);
-	if (projInfo->pi_lastScanVar > 0)
-		slot_getsomeattrs(econtext->ecxt_scantuple,
-						  projInfo->pi_lastScanVar);
-
-	/*
-	 * Assign simple Vars to result by direct extraction of fields from source
-	 * slots ... a mite ugly, but fast ...
-	 */
-	numSimpleVars = projInfo->pi_numSimpleVars;
-	if (numSimpleVars > 0)
-	{
-		Datum	   *values = slot->tts_values;
-		bool	   *isnull = slot->tts_isnull;
-		int		   *varSlotOffsets = projInfo->pi_varSlotOffsets;
-		int		   *varNumbers = projInfo->pi_varNumbers;
-		int			i;
-
-		if (projInfo->pi_directMap)
-		{
-			/* especially simple case where vars go to output in order */
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-
-				values[i] = varSlot->tts_values[varNumber];
-				isnull[i] = varSlot->tts_isnull[varNumber];
-			}
-		}
-		else
-		{
-			/* we have to pay attention to varOutputCols[] */
-			int		   *varOutputCols = projInfo->pi_varOutputCols;
-
-			for (i = 0; i < numSimpleVars; i++)
-			{
-				char	   *slotptr = ((char *) econtext) + varSlotOffsets[i];
-				TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
-				int			varNumber = varNumbers[i] - 1;
-				int			varOutputCol = varOutputCols[i] - 1;
-
-				values[varOutputCol] = varSlot->tts_values[varNumber];
-				isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
-			}
-		}
-	}
-
-	/*
-	 * If there are any generic expressions, evaluate them.  It's possible
-	 * that there are set-returning functions in such expressions; if so and
-	 * we have reached the end of the set, we return the result slot, which we
-	 * already marked empty.
-	 */
-	if (projInfo->pi_targetlist)
-	{
-		if (!ExecTargetList(projInfo->pi_targetlist,
-							slot->tts_tupleDescriptor,
-							econtext,
-							slot->tts_values,
-							slot->tts_isnull,
-							projInfo->pi_itemIsDone,
-							isDone))
-			return slot;		/* no more result rows, return empty slot */
-	}
-
-	/*
-	 * Successfully formed a result row.  Mark the result slot as containing a
-	 * valid virtual tuple.
-	 */
-	return ExecStoreVirtualTuple(slot);
-}
diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c
index fb0013d..1cb33ee 100644
--- a/src/backend/executor/execScan.c
+++ b/src/backend/executor/execScan.c
@@ -123,7 +123,7 @@ ExecScan(ScanState *node,
 		 ExecScanRecheckMtd recheckMtd)
 {
 	ExprContext *econtext;
-	List	   *qual;
+	ExprState2 *qual;
 	ProjectionInfo *projInfo;
 	ExprDoneCond isDone;
 	TupleTableSlot *resultSlot;
@@ -205,7 +205,7 @@ ExecScan(ScanState *node,
 		 * when the qual is nil ... saves only a few cycles, but they add up
 		 * ...
 		 */
-		if (!qual || ExecQual(qual, econtext, false))
+		if (!qual || ExecQual(qual))
 		{
 			/*
 			 * Found a satisfactory scan tuple.
diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index a3a59c0..0fa402f 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo
 	AttrNumber last_scan;
 } LastAttnumInfo;
 
-
 static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
 
 
@@ -475,142 +474,6 @@ ExecGetResultType(PlanState *planstate)
 	return slot->tts_tupleDescriptor;
 }
 
-/* ----------------
- *		ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot.  (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!)  Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor.  It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made.  At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
-						ExprContext *econtext,
-						TupleTableSlot *slot,
-						TupleDesc inputDesc)
-{
-	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
-	int			len = ExecTargetListLength(targetList);
-	int		   *workspace;
-	int		   *varSlotOffsets;
-	int		   *varNumbers;
-	int		   *varOutputCols;
-	List	   *exprlist;
-	int			numSimpleVars;
-	bool		directMap;
-	ListCell   *tl;
-
-	projInfo->pi_exprContext = econtext;
-	projInfo->pi_slot = slot;
-	/* since these are all int arrays, we need do just one palloc */
-	workspace = (int *) palloc(len * 3 * sizeof(int));
-	projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
-	projInfo->pi_varNumbers = varNumbers = workspace + len;
-	projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
-	projInfo->pi_lastInnerVar = 0;
-	projInfo->pi_lastOuterVar = 0;
-	projInfo->pi_lastScanVar = 0;
-
-	/*
-	 * We separate the target list elements into simple Var references and
-	 * expressions which require the full ExecTargetList machinery.  To be a
-	 * simple Var, a Var has to be a user attribute and not mismatch the
-	 * inputDesc.  (Note: if there is a type mismatch then ExecEvalScalarVar
-	 * will probably throw an error at runtime, but we leave that to it.)
-	 */
-	exprlist = NIL;
-	numSimpleVars = 0;
-	directMap = true;
-	foreach(tl, targetList)
-	{
-		GenericExprState *gstate = (GenericExprState *) lfirst(tl);
-		Var		   *variable = (Var *) gstate->arg->expr;
-		bool		isSimpleVar = false;
-
-		if (variable != NULL &&
-			IsA(variable, Var) &&
-			variable->varattno > 0)
-		{
-			if (!inputDesc)
-				isSimpleVar = true;		/* can't check type, assume OK */
-			else if (variable->varattno <= inputDesc->natts)
-			{
-				Form_pg_attribute attr;
-
-				attr = inputDesc->attrs[variable->varattno - 1];
-				if (!attr->attisdropped && variable->vartype == attr->atttypid)
-					isSimpleVar = true;
-			}
-		}
-
-		if (isSimpleVar)
-		{
-			TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
-			AttrNumber	attnum = variable->varattno;
-
-			varNumbers[numSimpleVars] = attnum;
-			varOutputCols[numSimpleVars] = tle->resno;
-			if (tle->resno != numSimpleVars + 1)
-				directMap = false;
-
-			switch (variable->varno)
-			{
-				case INNER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_innertuple);
-					if (projInfo->pi_lastInnerVar < attnum)
-						projInfo->pi_lastInnerVar = attnum;
-					break;
-
-				case OUTER_VAR:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_outertuple);
-					if (projInfo->pi_lastOuterVar < attnum)
-						projInfo->pi_lastOuterVar = attnum;
-					break;
-
-					/* INDEX_VAR is handled by default case */
-
-				default:
-					varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
-															 ecxt_scantuple);
-					if (projInfo->pi_lastScanVar < attnum)
-						projInfo->pi_lastScanVar = attnum;
-					break;
-			}
-			numSimpleVars++;
-		}
-		else
-		{
-			/* Not a simple variable, add it to generic targetlist */
-			exprlist = lappend(exprlist, gstate);
-			/* Examine expr to include contained Vars in lastXXXVar counts */
-			ExecGetLastAttnums((Node *) variable,
-							   &projInfo->pi_lastOuterVar,
-							   &projInfo->pi_lastInnerVar,
-							   &projInfo->pi_lastScanVar);
-		}
-	}
-	projInfo->pi_targetlist = exprlist;
-	projInfo->pi_numSimpleVars = numSimpleVars;
-	projInfo->pi_directMap = directMap;
-
-	if (exprlist == NIL)
-		projInfo->pi_itemIsDone = NULL; /* not needed */
-	else
-		projInfo->pi_itemIsDone = (ExprDoneCond *)
-			palloc(len * sizeof(ExprDoneCond));
-
-	return projInfo;
-}
-
 /*
  * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
  *
@@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc)
 {
 	planstate->ps_ProjInfo =
-		ExecBuildProjectionInfo(planstate->targetlist,
+		ExecBuildProjectionInfo(planstate->plan->targetlist,
 								planstate->ps_ExprContext,
 								planstate->ps_ResultTupleSlot,
+								planstate,
 								inputDesc);
 }
 
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
index b3187e6..5046da4 100644
--- a/src/backend/executor/nodeAgg.c
+++ b/src/backend/executor/nodeAgg.c
@@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate,
 static TupleTableSlot *
 project_aggregates(AggState *aggstate)
 {
-	ExprContext *econtext = aggstate->ss.ps.ps_ExprContext;
-
 	/*
 	 * Check the qual (HAVING clause); if the group does not match, ignore it.
 	 */
-	if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+	if (ExecQual(aggstate->ss.ps.qual))
 	{
 		/*
 		 * Form and return or store a projection tuple using the aggregate
@@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 	aggstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) aggstate);
-	aggstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) aggstate);
+	aggstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) aggstate,
+					 aggstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * Initialize child nodes.
@@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
 				 errmsg("aggregate function calls cannot be nested")));
 
 	/* Set up projection info for evaluation */
-	pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args,
+	pertrans->evalproj = ExecBuildProjectionInfo(aggref->args,
 												 aggstate->tmpcontext,
 												 pertrans->evalslot,
+												 (PlanState *) aggstate,
 												 NULL);
 
 	/*
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
index 449aacb..f6cc996 100644
--- a/src/backend/executor/nodeBitmapHeapscan.c
+++ b/src/backend/executor/nodeBitmapHeapscan.c
@@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
 
-			if (!ExecQual(node->bitmapqualorig, econtext, false))
+			if (!ExecQual(node->bitmapqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->bitmapqualorig, econtext, false);
+	return ExecQual(node->bitmapqualorig);
 }
 
 /* ----------------------------------------------------------------
@@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->bitmapqualorig = (List *)
-		ExecInitExpr((Expr *) node->bitmapqualorig,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->bitmapqualorig =
+		ExecInitQual(node->bitmapqualorig,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
index 3c2f684..f723f37 100644
--- a/src/backend/executor/nodeCtescan.c
+++ b/src/backend/executor/nodeCtescan.c
@@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c
index 322abca..bdd7039 100644
--- a/src/backend/executor/nodeCustom.c
+++ b/src/backend/executor/nodeCustom.c
@@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
 	css->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
 					 (PlanState *) css);
-	css->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) cscan->scan.plan.qual,
-					 (PlanState *) css);
+	css->ss.ps.qual =
+		ExecInitQual(cscan->scan.plan.qual,
+					 (PlanState *) css,
+					 css->ss.ps.ps_ExprContext);
 
 	/* tuple table initialization */
 	ExecInitScanTupleSlot(estate, &css->ss);
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
index d886aaf..387320e 100644
--- a/src/backend/executor/nodeForeignscan.c
+++ b/src/backend/executor/nodeForeignscan.c
@@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
 		!fdwroutine->RecheckForeignScan(node, slot))
 		return false;
 
-	return ExecQual(node->fdw_recheck_quals, econtext, false);
+	return ExecQual(node->fdw_recheck_quals);
 }
 
 /* ----------------------------------------------------------------
@@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
-	scanstate->fdw_recheck_quals = (List *)
-		ExecInitExpr((Expr *) node->fdw_recheck_quals,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
+	scanstate->fdw_recheck_quals =
+		ExecInitQual(node->fdw_recheck_quals,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
index a03f6e7..47b890a 100644
--- a/src/backend/executor/nodeFunctionscan.c
+++ b/src/backend/executor/nodeFunctionscan.c
@@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
 
diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c
index 313b234..1b933ae 100644
--- a/src/backend/executor/nodeGather.c
+++ b/src/backend/executor/nodeGather.c
@@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
 	gatherstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) gatherstate);
-	gatherstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) gatherstate);
+	gatherstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) gatherstate,
+					 gatherstate->ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
index dcf5175..1d61bc2 100644
--- a/src/backend/executor/nodeGroup.c
+++ b/src/backend/executor/nodeGroup.c
@@ -102,7 +102,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and fall into scan loop.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -165,7 +165,7 @@ ExecGroup(GroupState *node)
 		 * Check the qual (HAVING clause); if the group does not match, ignore
 		 * it and loop back to scan the rest of the group.
 		 */
-		if (ExecQual(node->ss.ps.qual, econtext, false))
+		if (ExecQual(node->ss.ps.qual))
 		{
 			/*
 			 * Form and return a projection tuple using the first input tuple.
@@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
 	grpstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) grpstate);
-	grpstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) grpstate);
+	grpstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) grpstate,
+					 grpstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
index 9ed09a7..85be8c2 100644
--- a/src/backend/executor/nodeHash.c
+++ b/src/backend/executor/nodeHash.c
@@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
 	hashstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) hashstate);
-	hashstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) hashstate);
+	hashstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) hashstate,
+					 hashstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -1064,7 +1065,7 @@ bool
 ExecScanHashBucket(HashJoinState *hjstate,
 				   ExprContext *econtext)
 {
-	List	   *hjclauses = hjstate->hashclauses;
+	ExprState2 *hjclauses = hjstate->hashclauses;
 	HashJoinTable hashtable = hjstate->hj_HashTable;
 	HashJoinTuple hashTuple = hjstate->hj_CurTuple;
 	uint32		hashvalue = hjstate->hj_CurHashValue;
@@ -1098,7 +1099,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
 			/* reset temp memory each time to avoid leaks from qual expr */
 			ResetExprContext(econtext);
 
-			if (ExecQual(hjclauses, econtext, false))
+			if (ExecQual(hjclauses))
 			{
 				hjstate->hj_CurTuple = hashTuple;
 				return true;
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 369e666..72044b6 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
 {
 	PlanState  *outerNode;
 	HashState  *hashNode;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ExprDoneCond isDone;
 	HashJoinTable hashtable;
@@ -293,7 +293,7 @@ ExecHashJoin(HashJoinState *node)
 				 * Only the joinquals determine tuple match status, but all
 				 * quals must pass to actually return the tuple.
 				 */
-				if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+				if (joinqual == NULL || ExecQual(joinqual))
 				{
 					node->hj_MatchedOuter = true;
 					HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@@ -312,8 +312,7 @@ ExecHashJoin(HashJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->hj_JoinState = HJ_NEED_NEW_OUTER;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node)
 					 */
 					econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
 
-					if (otherqual == NIL ||
-						ExecQual(otherqual, econtext, false))
+					if (otherqual == NULL || ExecQual(otherqual))
 					{
 						TupleTableSlot *result;
 
@@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node)
 				 */
 				econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
 
-				if (otherqual == NIL ||
-					ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					TupleTableSlot *result;
 
@@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	hjstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) hjstate);
-	hjstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) hjstate);
+	hjstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 	hjstate->js.jointype = node->join.jointype;
-	hjstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) hjstate);
-	hjstate->hashclauses = (List *)
-		ExecInitExpr((Expr *) node->hashclauses,
-					 (PlanState *) hjstate);
+	hjstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
+	hjstate->hashclauses =
+		ExecInitQual(node->hashclauses,
+					 (PlanState *) hjstate,
+					 hjstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
@@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
 	lclauses = NIL;
 	rclauses = NIL;
 	hoperators = NIL;
-	foreach(l, hjstate->hashclauses)
+	foreach(l, node->hashclauses)
 	{
-		FuncExprState *fstate = (FuncExprState *) lfirst(l);
-		OpExpr	   *hclause;
+		OpExpr	   *hclause =  (OpExpr *) lfirst(l);;
 
-		Assert(IsA(fstate, FuncExprState));
-		hclause = (OpExpr *) fstate->xprstate.expr;
 		Assert(IsA(hclause, OpExpr));
-		lclauses = lappend(lclauses, linitial(fstate->args));
-		rclauses = lappend(rclauses, lsecond(fstate->args));
+		lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+		rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
 		hoperators = lappend_oid(hoperators, hclause->opno);
 	}
 	hjstate->hj_OuterHashKeys = lclauses;
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
index 4f6f91c..a3e38df 100644
--- a/src/backend/executor/nodeIndexonlyscan.c
+++ b/src/backend/executor/nodeIndexonlyscan.c
@@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqual, econtext, false))
+			if (!ExecQual(node->indexqual))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqual = (List *)
-		ExecInitExpr((Expr *) node->indexqual,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqual =
+		ExecInitQual(node->indexqual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
index 3143bd9..b487119 100644
--- a/src/backend/executor/nodeIndexscan.c
+++ b/src/backend/executor/nodeIndexscan.c
@@ -122,7 +122,7 @@ IndexNext(IndexScanState *node)
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -241,7 +241,7 @@ next_indextuple:
 		{
 			econtext->ecxt_scantuple = slot;
 			ResetExprContext(econtext);
-			if (!ExecQual(node->indexqualorig, econtext, false))
+			if (!ExecQual(node->indexqualorig))
 			{
 				/* Fails recheck, so drop it and loop back for another */
 				InstrCountFiltered2(node, 1);
@@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
 
 	ResetExprContext(econtext);
 
-	return ExecQual(node->indexqualorig, econtext, false);
+	return ExecQual(node->indexqualorig);
 }
 
 
@@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
 	indexstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) indexstate);
-	indexstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) indexstate);
-	indexstate->indexqualorig = (List *)
-		ExecInitExpr((Expr *) node->indexqualorig,
-					 (PlanState *) indexstate);
+	indexstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
+	indexstate->indexqualorig =
+		ExecInitQual(node->indexqualorig,
+					 (PlanState *) indexstate,
+					 indexstate->ss.ps.ps_ExprContext);
 	indexstate->indexorderbyorig = (List *)
 		ExecInitExpr((Expr *) node->indexorderbyorig,
 					 (PlanState *) indexstate);
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 6db09b8..3e7c500 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -452,14 +452,14 @@ static TupleTableSlot *
 MJFillOuter(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -493,14 +493,14 @@ static TupleTableSlot *
 MJFillInner(MergeJoinState *node)
 {
 	ExprContext *econtext = node->js.ps.ps_ExprContext;
-	List	   *otherqual = node->js.ps.qual;
+	ExprState2 *otherqual = node->js.ps.qual;
 
 	ResetExprContext(econtext);
 
 	econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
 	econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
 
-	if (ExecQual(otherqual, econtext, false))
+	if (ExecQual(otherqual))
 	{
 		/*
 		 * qualification succeeded.  now form the desired projection tuple and
@@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
 TupleTableSlot *
 ExecMergeJoin(MergeJoinState *node)
 {
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	bool		qualResult;
 	int			compareResult;
 	PlanState  *innerPlan;
@@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node)
 				innerTupleSlot = node->mj_InnerTupleSlot;
 				econtext->ecxt_innertuple = innerTupleSlot;
 
-				qualResult = (joinqual == NIL ||
-							  ExecQual(joinqual, econtext, false));
+				qualResult = (joinqual == NULL || ExecQual(joinqual));
 				MJ_DEBUG_QUAL(joinqual, qualResult);
 
 				if (qualResult)
@@ -846,8 +845,7 @@ ExecMergeJoin(MergeJoinState *node)
 					if (node->js.jointype == JOIN_SEMI)
 						node->mj_JoinState = EXEC_MJ_NEXTOUTER;
 
-					qualResult = (otherqual == NIL ||
-								  ExecQual(otherqual, econtext, false));
+					qualResult = (otherqual == NULL || ExecQual(otherqual));
 					MJ_DEBUG_QUAL(otherqual, qualResult);
 
 					if (qualResult)
@@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
 	mergestate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) mergestate);
-	mergestate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) mergestate);
+	mergestate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->js.jointype = node->join.jointype;
-	mergestate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) mergestate);
+	mergestate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) mergestate,
+					 mergestate->js.ps.ps_ExprContext);
 	mergestate->mj_ConstFalseJoin = false;
 	/* mergeclauses are handled below */
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index af7b26c..8255688 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 {
 	ExprContext *econtext = mtstate->ps.ps_ExprContext;
 	Relation	relation = resultRelInfo->ri_RelationDesc;
-	List	   *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+	ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
 	HeapTupleData tuple;
 	HeapUpdateFailureData hufd;
 	LockTupleMode lockmode;
@@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
 	econtext->ecxt_innertuple = excludedSlot;
 	econtext->ecxt_outertuple = NULL;
 
-	if (!ExecQual(onConflictSetWhere, econtext, false))
+	if (!ExecQual(onConflictSetWhere))
 	{
 		ReleaseBuffer(buffer);
 		InstrCountFiltered1(&mtstate->ps, 1);
@@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(ll, wcoList)
 		{
 			WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
-			ExprState  *wcoExpr = ExecInitExpr((Expr *) wco->qual,
-											   mtstate->mt_plans[i]);
+			ExprState2  *wcoExpr = ExecInitQual((List *) wco->qual,
+												mtstate->mt_plans[i],
+												GetPerTupleExprContext(estate));
 
 			wcoExprs = lappend(wcoExprs, wcoExpr);
 		}
@@ -1687,8 +1688,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		slot = mtstate->ps.ps_ResultTupleSlot;
 
 		/* Need an econtext too */
-		econtext = CreateExprContext(estate);
-		mtstate->ps.ps_ExprContext = econtext;
+		if (mtstate->ps.ps_ExprContext == NULL)
+			ExecAssignExprContext(estate, &mtstate->ps);
+		econtext = mtstate->ps.ps_ExprContext;
 
 		/*
 		 * Build a projection for each result rel.
@@ -1697,12 +1699,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		foreach(l, node->returningLists)
 		{
 			List	   *rlist = (List *) lfirst(l);
-			List	   *rliststate;
 
-			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
 			resultRelInfo->ri_projectReturning =
-				ExecBuildProjectionInfo(rliststate, econtext, slot,
-									 resultRelInfo->ri_RelationDesc->rd_att);
+				ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+										resultRelInfo->ri_RelationDesc->rd_att);
 			resultRelInfo++;
 		}
 	}
@@ -1727,7 +1727,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 	if (node->onConflictAction == ONCONFLICT_UPDATE)
 	{
 		ExprContext *econtext;
-		ExprState  *setexpr;
 		TupleDesc	tupDesc;
 
 		/* insert may only have one plan, inheritance is not expanded */
@@ -1753,22 +1752,21 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
 		mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
 		ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
 
-		/* build UPDATE SET expression and projection state */
-		setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+		/* build UPDATE SET projection state */
 		resultRelInfo->ri_onConflictSetProj =
-			ExecBuildProjectionInfo((List *) setexpr, econtext,
-									mtstate->mt_conflproj,
+			ExecBuildProjectionInfo(node->onConflictSet, econtext,
+									mtstate->mt_conflproj, &mtstate->ps,
 									resultRelInfo->ri_RelationDesc->rd_att);
 
 		/* build DO UPDATE WHERE clause expression */
 		if (node->onConflictWhere)
 		{
-			ExprState  *qualexpr;
+			ExprState2 *qualexpr;
 
-			qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
-									&mtstate->ps);
+			qualexpr = ExecInitQual((List *) node->onConflictWhere,
+									&mtstate->ps, econtext);
 
-			resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+			resultRelInfo->ri_onConflictSetWhere = qualexpr;
 		}
 	}
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index 555fa09..6a8b674 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
 	PlanState  *outerPlan;
 	TupleTableSlot *outerTupleSlot;
 	TupleTableSlot *innerTupleSlot;
-	List	   *joinqual;
-	List	   *otherqual;
+	ExprState2 *joinqual;
+	ExprState2 *otherqual;
 	ExprContext *econtext;
 	ListCell   *lc;
 
@@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node)
 
 				ENL1_printf("testing qualification for outer-join tuple");
 
-				if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+				if (otherqual == NULL || ExecQual(otherqual))
 				{
 					/*
 					 * qualification was satisfied so we project and return
@@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node)
 					 * ExecProject().
 					 */
 					TupleTableSlot *result;
-					ExprDoneCond isDone;
+					ExprDoneCond isDone = ExprSingleResult;
 
 					ENL1_printf("qualification succeeded, projecting tuple");
 
-					result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
+					result = node->js.ps.ps_ResultTupleSlot;
+					ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result);
 
 					if (isDone != ExprEndResult)
 					{
@@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node)
 		 */
 		ENL1_printf("testing qualification");
 
-		if (ExecQual(joinqual, econtext, false))
+		if (ExecQual(joinqual))
 		{
 			node->nl_MatchedOuter = true;
 
@@ -253,7 +254,7 @@ ExecNestLoop(NestLoopState *node)
 			if (node->js.jointype == JOIN_SEMI)
 				node->nl_NeedNewOuter = true;
 
-			if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+			if (otherqual == NULL || ExecQual(otherqual))
 			{
 				/*
 				 * qualification was satisfied so we project and return the
@@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
 	nlstate->js.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->join.plan.targetlist,
 					 (PlanState *) nlstate);
-	nlstate->js.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->join.plan.qual,
-					 (PlanState *) nlstate);
+	nlstate->js.ps.qual =
+		ExecInitQual(node->join.plan.qual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 	nlstate->js.jointype = node->join.jointype;
-	nlstate->js.joinqual = (List *)
-		ExecInitExpr((Expr *) node->join.joinqual,
-					 (PlanState *) nlstate);
+	nlstate->js.joinqual =
+		ExecInitQual(node->join.joinqual,
+					 (PlanState *) nlstate,
+					 nlstate->js.ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
index 4007b76..41d53f2 100644
--- a/src/backend/executor/nodeResult.c
+++ b/src/backend/executor/nodeResult.c
@@ -79,9 +79,7 @@ ExecResult(ResultState *node)
 	 */
 	if (node->rs_checkqual)
 	{
-		bool		qualResult = ExecQual((List *) node->resconstantqual,
-										  econtext,
-										  false);
+		bool		qualResult = ExecQual(node->resconstantqual);
 
 		node->rs_checkqual = false;
 		if (!qualResult)
@@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags)
 	resstate->ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) resstate);
-	resstate->ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) resstate);
-	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
-											 (PlanState *) resstate);
+	resstate->ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) resstate,
+					 resstate->ps.ps_ExprContext);
+	resstate->resconstantqual =
+		ExecInitQual((List *) node->resconstantqual,
+					  (PlanState *) resstate,
+					  resstate->ps.ps_ExprContext);
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c
index 9ce7c02..6ebc330 100644
--- a/src/backend/executor/nodeSamplescan.c
+++ b/src/backend/executor/nodeSamplescan.c
@@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	scanstate->args = (List *)
 		ExecInitExpr((Expr *) tsc->args,
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
index 00bf3a5..13a84c5 100644
--- a/src/backend/executor/nodeSeqscan.c
+++ b/src/backend/executor/nodeSeqscan.c
@@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index e503494..30c0e33 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node,
 	/*
 	 * Evaluate lefthand expressions and form a projection tuple. First we
 	 * have to set the econtext to use (hack alert!).
+	 * FIXME: unlikely to still work
 	 */
+	elog(ERROR, "borked atm");
 	node->projLeft->pi_exprContext = econtext;
 	slot = ExecProject(node->projLeft, NULL);
 
@@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
 		 * Fortunately we can just pass NULL for now and fill it in later
 		 * (hack alert!).  The righthand expressions will be evaluated in our
 		 * own innerecontext.
+		 *
+		 * FIXME
 		 */
 		tupDesc = ExecTypeFromTL(leftptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
-												   NULL,
+		sstate->projLeft = ExecBuildProjectionInfo(leftptlist,
+												   sstate->innerecontext /* FIXME */,
 												   slot,
+												   sstate->planstate,
 												   NULL);
 
 		tupDesc = ExecTypeFromTL(rightptlist, false);
 		slot = ExecInitExtraTupleSlot(estate);
 		ExecSetSlotDescriptor(slot, tupDesc);
-		sstate->projRight = ExecBuildProjectionInfo(righttlist,
+		sstate->projRight = ExecBuildProjectionInfo(rightptlist,
 													sstate->innerecontext,
 													slot,
+													sstate->planstate,
 													NULL);
 	}
 
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
index 9bafc62..1e3a228 100644
--- a/src/backend/executor/nodeSubqueryscan.c
+++ b/src/backend/executor/nodeSubqueryscan.c
@@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
 	subquerystate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) subquerystate);
-	subquerystate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) subquerystate);
+	subquerystate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) subquerystate,
+					 subquerystate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
index 2604103..e47e280 100644
--- a/src/backend/executor/nodeTidscan.c
+++ b/src/backend/executor/nodeTidscan.c
@@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
 	tidstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) tidstate);
-	tidstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) tidstate);
+	tidstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) tidstate,
+					 tidstate->ss.ps.ps_ExprContext);
 
 	tidstate->tss_tidquals = (List *)
 		ExecInitExpr((Expr *) node->tidquals,
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
index 9c03f8a..0f3ef05 100644
--- a/src/backend/executor/nodeValuesscan.c
+++ b/src/backend/executor/nodeValuesscan.c
@@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
index d4c88a1..d2dfcb4 100644
--- a/src/backend/executor/nodeWindowAgg.c
+++ b/src/backend/executor/nodeWindowAgg.c
@@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
 	 * logical top level of a query (ie, after any WHERE or HAVING filters)
 	 */
 	Assert(node->plan.qual == NIL);
-	winstate->ss.ps.qual = NIL;
+	winstate->ss.ps.qual = NULL;
 
 	/*
 	 * initialize child nodes
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
index cfed6e6..352d1cd 100644
--- a/src/backend/executor/nodeWorktablescan.c
+++ b/src/backend/executor/nodeWorktablescan.c
@@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
 	scanstate->ss.ps.targetlist = (List *)
 		ExecInitExpr((Expr *) node->scan.plan.targetlist,
 					 (PlanState *) scanstate);
-	scanstate->ss.ps.qual = (List *)
-		ExecInitExpr((Expr *) node->scan.plan.qual,
-					 (PlanState *) scanstate);
+	scanstate->ss.ps.qual =
+		ExecInitQual(node->scan.plan.qual,
+					 (PlanState *) scanstate,
+					 scanstate->ss.ps.ps_ExprContext);
 
 	/*
 	 * tuple table initialization
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index d4a5e7c..4b9d0a2 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd)
 		"UPDATE pg_class "
 		"  SET relacl = (SELECT array_agg(a.acl) FROM "
 		" (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl "
-		"  UNION SELECT unnest(pg_catalog.acldefault("
-		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))"
+		"  UNION SELECT * FROM unnest(pg_catalog.acldefault("
+		"    CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un"
 		" ) as a) "
 		"  WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n",
 		"GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n",
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index e5c0a56..8766d09 100644
--- a/src/include/executor/executor.h
+++ b/src/include/executor/executor.h
@@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt
 						  bool *isNull, ExprDoneCond *isDone);
 extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
 extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context);
+extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context);
+
+extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull);
+extern bool ExecQual(ExprState2 *state);
+extern bool ExecCheck(ExprState2 *state);
 extern int	ExecTargetListLength(List *targetlist);
 extern int	ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo,
-			ExprDoneCond *isDone);
+
+extern void ExecProjectIntoSlot(ProjectionInfo *projInfo,
+								TupleTableSlot *slot);
+/*
+ * ExecProject
+ *
+ *		projects a tuple based on projection info and stores
+ *		it in the previously specified tuple table slot.
+ *
+ *		Note: the result is always a virtual tuple; therefore it
+ *		may reference the contents of the exprContext's scan tuples
+ *		and/or temporary results constructed in the exprContext.
+ *		If the caller wishes the result to be valid longer than that
+ *		data will be valid, he must call ExecMaterializeSlot on the
+ *		result slot.
+ */
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone)
+{
+	if (isDone)
+		*isDone = ExprSingleResult;
+	ExecProjectIntoSlot(projInfo, projInfo->pi_slot);
+	return projInfo->pi_slot;
+}
 
 /*
  * prototypes from functions in execScan.c
@@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node,
 extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
 						ExprContext *econtext,
 						TupleTableSlot *slot,
+						PlanState *planstate,
 						TupleDesc inputDesc);
 extern void ExecAssignProjectionInfo(PlanState *planstate,
 						 TupleDesc inputDesc);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..b083629 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -26,6 +26,112 @@
 #include "utils/tuplesort.h"
 
 
+typedef struct ExprState ExprState;
+typedef struct ExprState2 ExprState2;
+
+
+
+typedef enum ExprEvalOp
+{
+	EEO_VAR,
+	EEO_SYSVAR,
+	EEO_CONST,
+	EEO_FUNCEXPR,
+	EEO_FUNCEXPR_STRICT,
+	EEO_FUNCEXPR_FUSAGE,
+	EEO_FUNCEXPR_STRICT_FUSAGE,
+	EEO_BOOL_AND_STEP_FIRST,
+	EEO_BOOL_AND_STEP,
+	EEO_BOOL_OR_STEP_FIRST,
+	EEO_BOOL_OR_STEP,
+	EEO_BOOL_NOT_STEP,
+	EEO_FETCHSOMEATTRS,
+	EEO_ASSIGN_VAR,
+	EEO_ASSIGN_TMP,
+	EEO_EVAL1,
+	EEO_ASSIGN_EVAL1,
+	EEO_DONE
+} ExprEvalOp;
+
+typedef struct ExprEvalStep
+{
+	ExprEvalOp opcode;
+	bool *resnull;
+	Datum *resvalue;
+	union
+	{
+		struct
+		{
+			TupleTableSlot **slot;
+			int attnum;
+		} var;
+
+		struct
+		{
+			Datum value;
+			bool isnull;
+		} constval;
+
+		struct
+		{
+			FmgrInfo	*finfo;
+			FunctionCallInfo fcinfo_data;
+			PGFunction	fn_addr;
+			int nargs;
+		} func;
+
+		struct
+		{
+			Datum *value;
+			bool *isnull;
+			bool *anynull;
+			int jumpdone;
+		} boolexpr;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			size_t resultnum;
+			int attnum;
+		} assign_var;
+
+		struct
+		{
+			size_t resultnum;
+		} assign_tmp;
+
+		struct
+		{
+			TupleTableSlot **slot;
+			int last_var;
+		} fetch;
+
+		struct
+		{
+			ExprState *expr;
+		} eval1;
+
+		struct
+		{
+			ExprState *expr;
+			size_t resultnum;
+		} assign_eval1;
+	} d;
+} ExprEvalStep;
+
+struct ExprState2
+{
+	Node tag;
+	bool failed;
+	size_t steps_alloc;
+	size_t steps_len;
+	ExprEvalStep *steps;
+	Datum resvalue;
+	bool resnull;
+	struct ExprContext *econtext;
+	TupleTableSlot *resultslot;
+};
+
 /* ----------------
  *	  IndexInfo information
  *
@@ -63,7 +169,7 @@ typedef struct IndexInfo
 	List	   *ii_Expressions; /* list of Expr */
 	List	   *ii_ExpressionsState;	/* list of ExprState */
 	List	   *ii_Predicate;	/* list of Expr */
-	List	   *ii_PredicateState;		/* list of ExprState */
+	ExprState2 *ii_PredicateState;
 	Oid		   *ii_ExclusionOps;	/* array with one entry per column */
 	Oid		   *ii_ExclusionProcs;		/* array with one entry per column */
 	uint16	   *ii_ExclusionStrats;		/* array with one entry per column */
@@ -205,53 +311,16 @@ typedef struct ReturnSetInfo
  *		that is, form new tuples by evaluation of targetlist expressions.
  *		Nodes which need to do projections create one of these.
  *
- *		ExecProject() evaluates the tlist, forms a tuple, and stores it
- *		in the given slot.  Note that the result will be a "virtual" tuple
- *		unless ExecMaterializeSlot() is then called to force it to be
- *		converted to a physical tuple.  The slot must have a tupledesc
- *		that matches the output of the tlist!
- *
- *		The planner very often produces tlists that consist entirely of
- *		simple Var references (lower levels of a plan tree almost always
- *		look like that).  And top-level tlists are often mostly Vars too.
- *		We therefore optimize execution of simple-Var tlist entries.
- *		The pi_targetlist list actually contains only the tlist entries that
- *		aren't simple Vars, while those that are Vars are processed using the
- *		varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- *		The lastXXXVar fields are used to optimize fetching of fields from
- *		input tuples: they let us do a slot_getsomeattrs() call to ensure
- *		that all needed attributes are extracted in one pass.
- *
- *		targetlist		target list for projection (non-Var expressions only)
  *		exprContext		expression context in which to evaluate targetlist
  *		slot			slot to place projection result in
- *		itemIsDone		workspace array for ExecProject
- *		directMap		true if varOutputCols[] is an identity map
- *		numSimpleVars	number of simple Vars found in original tlist
- *		varSlotOffsets	array indicating which slot each simple Var is from
- *		varNumbers		array containing input attr numbers of simple Vars
- *		varOutputCols	array containing output attr numbers of simple Vars
- *		lastInnerVar	highest attnum from inner tuple slot (0 if none)
- *		lastOuterVar	highest attnum from outer tuple slot (0 if none)
- *		lastScanVar		highest attnum from scan tuple slot (0 if none)
  * ----------------
  */
 typedef struct ProjectionInfo
 {
 	NodeTag		type;
-	List	   *pi_targetlist;
+	ExprState2 pi_state;
 	ExprContext *pi_exprContext;
 	TupleTableSlot *pi_slot;
-	ExprDoneCond *pi_itemIsDone;
-	bool		pi_directMap;
-	int			pi_numSimpleVars;
-	int		   *pi_varSlotOffsets;
-	int		   *pi_varNumbers;
-	int		   *pi_varOutputCols;
-	int			pi_lastInnerVar;
-	int			pi_lastOuterVar;
-	int			pi_lastScanVar;
 } ProjectionInfo;
 
 /* ----------------
@@ -331,18 +400,18 @@ typedef struct ResultRelInfo
 	IndexInfo **ri_IndexRelationInfo;
 	TriggerDesc *ri_TrigDesc;
 	FmgrInfo   *ri_TrigFunctions;
-	List	  **ri_TrigWhenExprs;
+	ExprState2 **ri_TrigWhenExprs;
 	Instrumentation *ri_TrigInstrument;
 	struct FdwRoutine *ri_FdwRoutine;
 	void	   *ri_FdwState;
 	bool		ri_usesFdwDirectModify;
 	List	   *ri_WithCheckOptions;
 	List	   *ri_WithCheckOptionExprs;
-	List	  **ri_ConstraintExprs;
+	ExprState2 **ri_ConstraintExprs;
 	JunkFilter *ri_junkFilter;
 	ProjectionInfo *ri_projectReturning;
 	ProjectionInfo *ri_onConflictSetProj;
-	List	   *ri_onConflictSetWhere;
+	ExprState2 *ri_onConflictSetWhere;
 } ResultRelInfo;
 
 /* ----------------
@@ -1039,7 +1108,7 @@ typedef struct PlanState
 	 * subPlan list, which does not exist in the plan tree).
 	 */
 	List	   *targetlist;		/* target list to be computed at this node */
-	List	   *qual;			/* implicitly-ANDed qual conditions */
+	ExprState2 *qual;			/* implicitly-ANDed qual conditions */
 	struct PlanState *lefttree; /* input plan tree(s) */
 	struct PlanState *righttree;
 	List	   *initPlan;		/* Init SubPlanState nodes (un-correlated expr
@@ -1106,7 +1175,7 @@ typedef struct EPQState
 typedef struct ResultState
 {
 	PlanState	ps;				/* its first field is NodeTag */
-	ExprState  *resconstantqual;
+	ExprState2 *resconstantqual;
 	bool		rs_done;		/* are we done? */
 	bool		rs_checkqual;	/* do we need to check the qual? */
 } ResultState;
@@ -1329,7 +1398,7 @@ typedef struct
 typedef struct IndexScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqualorig;
+	ExprState2 *indexqualorig;
 	List	   *indexorderbyorig;
 	ScanKey		iss_ScanKeys;
 	int			iss_NumScanKeys;
@@ -1373,7 +1442,7 @@ typedef struct IndexScanState
 typedef struct IndexOnlyScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *indexqual;
+	ExprState2 *indexqual;
 	ScanKey		ioss_ScanKeys;
 	int			ioss_NumScanKeys;
 	ScanKey		ioss_OrderByKeys;
@@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState
 typedef struct BitmapHeapScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *bitmapqualorig;
+	ExprState2 *bitmapqualorig;
 	TIDBitmap  *tbm;
 	TBMIterator *tbmiterator;
 	TBMIterateResult *tbmres;
@@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState
 typedef struct TidScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *tss_tidquals;	/* list of ExprState nodes */
+	List	   *tss_tidquals;
 	bool		tss_isCurrentOf;
 	int			tss_NumTids;
 	int			tss_TidPtr;
@@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState
 typedef struct ForeignScanState
 {
 	ScanState	ss;				/* its first field is NodeTag */
-	List	   *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
+	ExprState2 *fdw_recheck_quals;		/* original quals not in ss.ps.qual */
 	Size		pscan_len;		/* size of parallel coordination information */
 	/* use struct pointer to avoid including fdwapi.h here */
 	struct FdwRoutine *fdwroutine;
@@ -1632,7 +1701,7 @@ typedef struct JoinState
 {
 	PlanState	ps;
 	JoinType	jointype;
-	List	   *joinqual;		/* JOIN quals (in addition to ps.qual) */
+	ExprState2 *joinqual;		/* JOIN quals (in addition to ps.qual) */
 } JoinState;
 
 /* ----------------
@@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable;
 typedef struct HashJoinState
 {
 	JoinState	js;				/* its first field is NodeTag */
-	List	   *hashclauses;	/* list of ExprState nodes */
+	ExprState2 *hashclauses;
 	List	   *hj_OuterHashKeys;		/* list of ExprState nodes */
 	List	   *hj_InnerHashKeys;		/* list of ExprState nodes */
 	List	   *hj_HashOperators;		/* list of operator OIDs */
@@ -1852,6 +1921,12 @@ typedef struct AggState
 	/* these fields are used in AGG_HASHED mode: */
 	TupleHashTable hashtable;	/* hash table with one entry per group */
 	TupleTableSlot *hashslot;	/* slot for loading hash table */
+
+	/* Stuff for evaluation of agg inputs. */
+	TupleTableSlot *evalslot;	/* slot for agg inputs */
+	ProjectionInfo *evalproj;	/* projection machinery */
+	TupleDesc	evaldesc;		/* descriptor of input tuples */
+
 	List	   *hash_needed;	/* list of columns needed in hash table */
 	bool		table_filled;	/* hash table filled yet? */
 	TupleHashIterator hashiter; /* for iterating through hash table */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 6b850e4..0f503d3 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -33,6 +33,7 @@ typedef enum NodeTag
 	T_IndexInfo = 10,
 	T_ExprContext,
 	T_ProjectionInfo,
+	T_FastProjectionInfo,
 	T_JunkFilter,
 	T_ResultRelInfo,
 	T_EState,
@@ -188,6 +189,7 @@ typedef enum NodeTag
 	 * from Expr.
 	 */
 	T_ExprState = 400,
+	T_ExprState2,
 	T_GenericExprState,
 	T_WholeRowVarExprState,
 	T_AggrefExprState,
-- 
2.8.1


--jfd4diy4zttxubog
Content-Type: text/x-patch; charset=us-ascii
Content-Disposition: attachment;
 filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch"



^ permalink  raw  reply  [nested|flat] 21+ messages in thread

* [PATCH v32 09/11] Add support for min/max aggregates for IVM
@ 2023-05-31 11:58 Yugo Nagata <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Yugo Nagata @ 2023-05-31 11:58 UTC (permalink / raw)

Supporting min and max is more complicated than count, sum, or avg.

For an example of min, when tuples are inserted, the current min value
in the view and the min value in the inseteted tuples are compared,
then the smaller one is used as the latest min value. On the other
hand, when tuples are deleted, if the current min value in the view
equals to the min in the deleted tuples, we need re-computation the
latest min value from base tables. Otherwise, the current value in
the view remains.
---
 src/backend/commands/createas.c |  45 +++
 src/backend/commands/matview.c  | 644 +++++++++++++++++++++++++++++++-
 2 files changed, 680 insertions(+), 9 deletions(-)

diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index ecec93ec1c..d3cf3df5a7 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -1311,6 +1311,51 @@ check_aggregate_supports_ivm(Oid aggfnoid)
 		case F_AVG_FLOAT8:
 		case F_AVG_INTERVAL:
 
+		/* min */
+		case F_MIN_ANYARRAY:
+		case F_MIN_INT8:
+		case F_MIN_INT4:
+		case F_MIN_INT2:
+		case F_MIN_OID:
+		case F_MIN_FLOAT4:
+		case F_MIN_FLOAT8:
+		case F_MIN_DATE:
+		case F_MIN_TIME:
+		case F_MIN_TIMETZ:
+		case F_MIN_MONEY:
+		case F_MIN_TIMESTAMP:
+		case F_MIN_TIMESTAMPTZ:
+		case F_MIN_INTERVAL:
+		case F_MIN_TEXT:
+		case F_MIN_NUMERIC:
+		case F_MIN_BPCHAR:
+		case F_MIN_TID:
+		case F_MIN_ANYENUM:
+		case F_MIN_INET:
+		case F_MIN_PG_LSN:
+
+		/* max */
+		case F_MAX_ANYARRAY:
+		case F_MAX_INT8:
+		case F_MAX_INT4:
+		case F_MAX_INT2:
+		case F_MAX_OID:
+		case F_MAX_FLOAT4:
+		case F_MAX_FLOAT8:
+		case F_MAX_DATE:
+		case F_MAX_TIME:
+		case F_MAX_TIMETZ:
+		case F_MAX_MONEY:
+		case F_MAX_TIMESTAMP:
+		case F_MAX_TIMESTAMPTZ:
+		case F_MAX_INTERVAL:
+		case F_MAX_TEXT:
+		case F_MAX_NUMERIC:
+		case F_MAX_BPCHAR:
+		case F_MAX_TID:
+		case F_MAX_ANYENUM:
+		case F_MAX_INET:
+		case F_MAX_PG_LSN:
 			return true;
 
 		default:
diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c
index b7f6c3831b..42ff0ed98f 100644
--- a/src/backend/commands/matview.c
+++ b/src/backend/commands/matview.c
@@ -69,6 +69,34 @@ typedef struct
 
 #define MV_INIT_QUERYHASHSIZE	16
 
+/* MV query type codes */
+#define MV_PLAN_RECALC			1
+#define MV_PLAN_SET_VALUE		2
+
+/*
+ * MI_QueryKey
+ *
+ * The key identifying a prepared SPI plan in our query hashtable
+ */
+typedef struct MV_QueryKey
+{
+	Oid			matview_id;	/* OID of materialized view */
+	int32		query_type;	/* query type ID, see MV_PLAN_XXX above */
+} MV_QueryKey;
+
+/*
+ * MV_QueryHashEntry
+ *
+ * Hash entry for cached plans used to maintain materialized views.
+ */
+typedef struct MV_QueryHashEntry
+{
+	MV_QueryKey key;
+	SPIPlanPtr	plan;
+	SearchPathMatcher *search_path;        /* search_path used for parsing
+											 * and planning */
+} MV_QueryHashEntry;
+
 /*
  * MV_TriggerHashEntry
  *
@@ -105,6 +133,7 @@ typedef struct MV_TriggerTable
 	TupleTableSlot *slot;		/* for checking visibility in the pre-state table */
 } MV_TriggerTable;
 
+static HTAB *mv_query_cache = NULL;
 static HTAB *mv_trigger_info = NULL;
 
 static bool in_delta_calculation = false;
@@ -165,6 +194,9 @@ static void append_set_clause_for_sum(const char *resname, StringInfo buf_old,
 static void append_set_clause_for_avg(const char *resname, StringInfo buf_old,
 						  StringInfo buf_new, StringInfo aggs_list,
 						  const char *aggtype);
+static void append_set_clause_for_minmax(const char *resname, StringInfo buf_old,
+							 StringInfo buf_new, StringInfo aggs_list,
+							 bool is_min);
 static char *get_operation_string(IvmOp op, const char *col, const char *arg1, const char *arg2,
 					 const char* count_col, const char *castType);
 static char *get_null_condition_string(IvmOp op, const char *arg1, const char *arg2,
@@ -173,17 +205,30 @@ static void apply_old_delta(const char *matviewname, const char *deltaname_old,
 				List *keys);
 static void apply_old_delta_with_count(const char *matviewname, const char *deltaname_old,
 				List *keys, StringInfo aggs_list, StringInfo aggs_set,
-				const char *count_colname);
+				List *minmax_list, List *is_min_list,
+				const char *count_colname,
+				SPITupleTable **tuptable_recalc, uint64 *num_recalc);
 static void apply_new_delta(const char *matviewname, const char *deltaname_new,
 				StringInfo target_list);
 static void apply_new_delta_with_count(const char *matviewname, const char* deltaname_new,
 				List *keys, StringInfo target_list, StringInfo aggs_set,
 				const char* count_colname);
 static char *get_matching_condition_string(List *keys);
+static char *get_returning_string(List *minmax_list, List *is_min_list, List *keys);
+static char *get_minmax_recalc_condition_string(List *minmax_list, List *is_min_list);
+static char *get_select_for_recalc_string(List *keys);
+static void recalc_and_set_values(SPITupleTable *tuptable_recalc, int64 num_tuples,
+					  List *namelist, List *keys, Relation matviewRel);
+static SPIPlanPtr get_plan_for_recalc(Oid matviewOid, List *namelist, List *keys, Oid *keyTypes);
+static SPIPlanPtr get_plan_for_set_values(Oid matviewOid, char *matviewname, List *namelist,
+						Oid *valTypes);
 static void generate_equal(StringInfo querybuf, Oid opttype,
 			   const char *leftop, const char *rightop);
 
 static void mv_InitHashTables(void);
+static SPIPlanPtr mv_FetchPreparedPlan(MV_QueryKey *key);
+static void mv_HashPreparedPlan(MV_QueryKey *key, SPIPlanPtr plan);
+static void mv_BuildQueryKey(MV_QueryKey *key, Oid matview_id, int32 query_type);
 static void clean_up_IVM_hash_entry(MV_TriggerHashEntry *entry, bool is_abort);
 
 /*
@@ -2124,6 +2169,8 @@ apply_delta(Oid matviewOid, Tuplestorestate *old_tuplestores, Tuplestorestate *n
 	ListCell	*lc;
 	int			i;
 	List	   *keys = NIL;
+	List	   *minmax_list = NIL;
+	List	   *is_min_list = NIL;
 
 
 	/*
@@ -2205,6 +2252,17 @@ apply_delta(Oid matviewOid, Tuplestorestate *old_tuplestores, Tuplestorestate *n
 				append_set_clause_for_avg(resname, aggs_set_old, aggs_set_new, aggs_list_buf,
 										  format_type_be(aggref->aggtype));
 
+			/* min/max */
+			else if (!strcmp(aggname, "min") || !strcmp(aggname, "max"))
+			{
+				bool	is_min = (!strcmp(aggname, "min"));
+
+				append_set_clause_for_minmax(resname, aggs_set_old, aggs_set_new, aggs_list_buf, is_min);
+
+				/* make a resname list of min and max aggregates */
+				minmax_list = lappend(minmax_list, resname);
+				is_min_list = lappend_int(is_min_list, is_min);
+			}
 			else
 				elog(ERROR, "unsupported aggregate function: %s", aggname);
 		}
@@ -2234,6 +2292,8 @@ apply_delta(Oid matviewOid, Tuplestorestate *old_tuplestores, Tuplestorestate *n
 	if (old_tuplestores && tuplestore_tuple_count(old_tuplestores) > 0)
 	{
 		EphemeralNamedRelation enr = palloc(sizeof(EphemeralNamedRelationData));
+		SPITupleTable  *tuptable_recalc = NULL;
+		uint64			num_recalc;
 		int				rc;
 
 		/* convert tuplestores to ENR, and register for SPI */
@@ -2252,10 +2312,18 @@ apply_delta(Oid matviewOid, Tuplestorestate *old_tuplestores, Tuplestorestate *n
 			/* apply old delta and get rows to be recalculated */
 			apply_old_delta_with_count(matviewname, OLD_DELTA_ENRNAME,
 									   keys, aggs_list_buf, aggs_set_old,
-									   count_colname);
+									   minmax_list, is_min_list,
+									   count_colname, &tuptable_recalc, &num_recalc);
 		else
 			apply_old_delta(matviewname, OLD_DELTA_ENRNAME, keys);
 
+		/*
+		 * If we have min or max, we might have to recalculate aggregate values from base tables
+		 * on some tuples. TIDs and keys such tuples are returned as a result of the above query.
+		 */
+		if (minmax_list && tuptable_recalc)
+			recalc_and_set_values(tuptable_recalc, num_recalc, minmax_list, keys, matviewRel);
+
 	}
 	/* For tuple insertion */
 	if (new_tuplestores && tuplestore_tuple_count(new_tuplestores) > 0)
@@ -2447,6 +2515,70 @@ append_set_clause_for_avg(const char *resname, StringInfo buf_old,
 	);
 }
 
+/*
+ * append_set_clause_for_minmax
+ *
+ * Append SET clause string for min or max aggregation to given buffers.
+ * Also, append resnames required for calculating the aggregate value.
+ * is_min is true if this is min, false if not.
+ */
+static void
+append_set_clause_for_minmax(const char *resname, StringInfo buf_old,
+							 StringInfo buf_new, StringInfo aggs_list,
+							 bool is_min)
+{
+	char *count_col = IVM_colname("count", resname);
+
+	/* For tuple deletion */
+	if (buf_old)
+	{
+		/*
+		 * If the new value doesn't became NULL then use the value remaining
+		 * in the view although this will be recomputated afterwords.
+		 */
+		appendStringInfo(buf_old,
+			", %s = CASE WHEN %s THEN NULL ELSE %s END",
+			quote_qualified_identifier(NULL, resname),
+			get_null_condition_string(IVM_SUB, "mv", "t", count_col),
+			quote_qualified_identifier("mv", resname)
+		);
+		/* count = mv.count - t.count */
+		appendStringInfo(buf_old,
+			", %s = %s",
+			quote_qualified_identifier(NULL, count_col),
+			get_operation_string(IVM_SUB, count_col, "mv", "t", NULL, NULL)
+		);
+	}
+	/* For tuple insertion */
+	if (buf_new)
+	{
+		/*
+		 * min = LEAST(mv.min, diff.min)
+		 * max = GREATEST(mv.max, diff.max)
+		 */
+		appendStringInfo(buf_new,
+			", %s = CASE WHEN %s THEN NULL ELSE %s(%s,%s) END",
+			quote_qualified_identifier(NULL, resname),
+			get_null_condition_string(IVM_ADD, "mv", "diff", count_col),
+
+			is_min ? "LEAST" : "GREATEST",
+			quote_qualified_identifier("mv", resname),
+			quote_qualified_identifier("diff", resname)
+		);
+		/* count = mv.count + diff.count */
+		appendStringInfo(buf_new,
+			", %s = %s",
+			quote_qualified_identifier(NULL, count_col),
+			get_operation_string(IVM_ADD, count_col, "mv", "diff", NULL, NULL)
+		);
+	}
+
+	appendStringInfo(aggs_list, ", %s, %s",
+		quote_qualified_identifier("diff", resname),
+		quote_qualified_identifier("diff", IVM_colname("count", resname))
+	);
+}
+
 /*
  * get_operation_string
  *
@@ -2549,19 +2681,44 @@ get_null_condition_string(IvmOp op, const char *arg1, const char *arg2,
  * list to identify a tuple in the view. If the view has aggregates, this
  * requires strings representing resnames of aggregates and SET clause for
  * updating aggregate values.
+ *
+ * If the view has min or max aggregate, this requires a list of resnames of
+ * min/max aggregates and a list of boolean which represents which entries in
+ * minmax_list is min. These are necessary to check if we need to recalculate
+ * min or max aggregate values. In this case, this query returns TID and keys
+ * of tuples which need to be recalculated.  This result and the number of rows
+ * are stored in tuptables and num_recalc repectedly.
+ *
  */
 static void
 apply_old_delta_with_count(const char *matviewname, const char *deltaname_old,
 				List *keys, StringInfo aggs_list, StringInfo aggs_set,
-				const char *count_colname)
+				List *minmax_list, List *is_min_list,
+				const char *count_colname,
+				SPITupleTable **tuptable_recalc, uint64 *num_recalc)
 {
 	StringInfoData	querybuf;
 	char   *match_cond;
+	char   *updt_returning = "";
+	char   *select_for_recalc = "SELECT";
 	bool	agg_without_groupby = (list_length(keys) == 0);
 
+	Assert(tuptable_recalc != NULL);
+	Assert(num_recalc != NULL);
+
 	/* build WHERE condition for searching tuples to be deleted */
 	match_cond = get_matching_condition_string(keys);
 
+	/*
+	 * We need a special RETURNING clause and SELECT statement for min/max to
+	 * check which tuple needs re-calculation from base tables.
+	 */
+	if (minmax_list)
+	{
+		updt_returning = get_returning_string(minmax_list, is_min_list, keys);
+		select_for_recalc = get_select_for_recalc_string(keys);
+	}
+
 	/* Search for matching tuples from the view and update or delete if found. */
 	initStringInfo(&querybuf);
 	appendStringInfo(&querybuf,
@@ -2576,10 +2733,11 @@ apply_old_delta_with_count(const char *matviewname, const char *deltaname_old,
 						"UPDATE %s AS mv SET %s = mv.%s OPERATOR(pg_catalog.-) t.%s "
 											"%s"	/* SET clauses for aggregates */
 						"FROM t WHERE mv.ctid OPERATOR(pg_catalog.=) t.ctid AND NOT for_dlt "
-					")"
-					/* delete a tuple if this is to be deleted */
-					"DELETE FROM %s AS mv USING t "
-					"WHERE mv.ctid OPERATOR(pg_catalog.=) t.ctid AND for_dlt",
+						"%s"						/* RETURNING clause for recalc infomation */
+					"), dlt AS ("			/* delete a tuple if this is to be deleted */
+						"DELETE FROM %s AS mv USING t "
+						"WHERE mv.ctid OPERATOR(pg_catalog.=) t.ctid AND for_dlt"
+					") %s",							/* SELECT returning which tuples need to be recalculated */
 					count_colname,
 					count_colname, count_colname, (agg_without_groupby ? "false" : "true"),
 					(aggs_list != NULL ? aggs_list->data : ""),
@@ -2587,10 +2745,25 @@ apply_old_delta_with_count(const char *matviewname, const char *deltaname_old,
 					match_cond,
 					matviewname, count_colname, count_colname, count_colname,
 					(aggs_set != NULL ? aggs_set->data : ""),
-					matviewname);
+					updt_returning,
+					matviewname,
+					select_for_recalc);
 
-	if (SPI_exec(querybuf.data, 0) != SPI_OK_DELETE)
+	if (SPI_exec(querybuf.data, 0) != SPI_OK_SELECT)
 		elog(ERROR, "SPI_exec failed: %s", querybuf.data);
+
+
+	/* Return tuples to be recalculated. */
+	if (minmax_list)
+	{
+		*tuptable_recalc = SPI_tuptable;
+		*num_recalc = SPI_processed;
+	}
+	else
+	{
+		*tuptable_recalc = NULL;
+		*num_recalc = 0;
+	}
 }
 
 /*
@@ -2773,6 +2946,349 @@ get_matching_condition_string(List *keys)
 	return match_cond.data;
 }
 
+/*
+ * get_returning_string
+ *
+ * Build a string for RETURNING clause of UPDATE used in apply_old_delta_with_count.
+ * This clause returns ctid and a boolean value that indicates if we need to
+ * recalculate min or max value, for each updated row.
+ */
+static char *
+get_returning_string(List *minmax_list, List *is_min_list, List *keys)
+{
+	StringInfoData returning;
+	char		*recalc_cond;
+	ListCell	*lc;
+
+	Assert(minmax_list != NIL && is_min_list != NIL);
+	recalc_cond = get_minmax_recalc_condition_string(minmax_list, is_min_list);
+
+	initStringInfo(&returning);
+
+	appendStringInfo(&returning, "RETURNING mv.ctid AS tid, (%s) AS recalc", recalc_cond);
+	foreach (lc, keys)
+	{
+		Form_pg_attribute attr = (Form_pg_attribute) lfirst(lc);
+		char *resname = NameStr(attr->attname);
+		appendStringInfo(&returning, ", %s", quote_qualified_identifier("mv", resname));
+	}
+
+	return returning.data;
+}
+
+/*
+ * get_minmax_recalc_condition_string
+ *
+ * Build a predicate string for checking if any min/max aggregate
+ * value needs to be recalculated.
+ */
+static char *
+get_minmax_recalc_condition_string(List *minmax_list, List *is_min_list)
+{
+	StringInfoData recalc_cond;
+	ListCell	*lc1, *lc2;
+
+	initStringInfo(&recalc_cond);
+
+	Assert (list_length(minmax_list) == list_length(is_min_list));
+
+	forboth (lc1, minmax_list, lc2, is_min_list)
+	{
+		char   *resname = (char *) lfirst(lc1);
+		bool	is_min = (bool) lfirst_int(lc2);
+		char   *op_str = (is_min ? ">=" : "<=");
+
+		appendStringInfo(&recalc_cond, "%s OPERATOR(pg_catalog.%s) %s",
+			quote_qualified_identifier("mv", resname),
+			op_str,
+			quote_qualified_identifier("t", resname)
+		);
+
+		if (lnext(minmax_list, lc1))
+			appendStringInfo(&recalc_cond, " OR ");
+	}
+
+	return recalc_cond.data;
+}
+
+/*
+ * get_select_for_recalc_string
+ *
+ * Build a query to return tid and keys of tuples which need
+ * recalculation. This is used as the result of the query
+ * built by apply_old_delta.
+ */
+static char *
+get_select_for_recalc_string(List *keys)
+{
+	StringInfoData qry;
+	ListCell	*lc;
+
+	initStringInfo(&qry);
+
+	appendStringInfo(&qry, "SELECT tid");
+	foreach (lc, keys)
+	{
+		Form_pg_attribute attr = (Form_pg_attribute) lfirst(lc);
+		appendStringInfo(&qry, ", %s", NameStr(attr->attname));
+	}
+
+	appendStringInfo(&qry, " FROM updt WHERE recalc");
+
+	return qry.data;
+}
+
+/*
+ * recalc_and_set_values
+ *
+ * Recalculate tuples in a materialized from base tables and update these.
+ * The tuples which needs recalculation are specified by keys, and resnames
+ * of columns to be updated are specified by namelist. TIDs and key values
+ * are given by tuples in tuptable_recalc. Its first attribute must be TID
+ * and key values must be following this.
+ */
+static void
+recalc_and_set_values(SPITupleTable *tuptable_recalc, int64 num_tuples,
+					  List *namelist, List *keys, Relation matviewRel)
+{
+	TupleDesc   tupdesc_recalc = tuptable_recalc->tupdesc;
+	Oid		   *keyTypes = NULL, *types = NULL;
+	char	   *keyNulls = NULL, *nulls = NULL;
+	Datum	   *keyVals = NULL, *vals = NULL;
+	int			num_vals = list_length(namelist);
+	int			num_keys = list_length(keys);
+	uint64      i;
+	Oid			matviewOid;
+	char	   *matviewname;
+
+	matviewOid = RelationGetRelid(matviewRel);
+	matviewname = quote_qualified_identifier(get_namespace_name(RelationGetNamespace(matviewRel)),
+											 RelationGetRelationName(matviewRel));
+
+	/* If we have keys, initialize arrays for them. */
+	if (keys)
+	{
+		keyTypes = palloc(sizeof(Oid) * num_keys);
+		keyNulls = palloc(sizeof(char) * num_keys);
+		keyVals = palloc(sizeof(Datum) * num_keys);
+		/* a tuple contains keys to be recalculated and ctid to be updated*/
+		Assert(tupdesc_recalc->natts == num_keys + 1);
+
+		/* Types of key attributes  */
+		for (i = 0; i < num_keys; i++)
+			keyTypes[i] = TupleDescAttr(tupdesc_recalc, i + 1)->atttypid;
+	}
+
+	/* allocate memory for all attribute names and tid */
+	types = palloc(sizeof(Oid) * (num_vals + 1));
+	nulls = palloc(sizeof(char) * (num_vals + 1));
+	vals = palloc(sizeof(Datum) * (num_vals + 1));
+
+	/* For each tuple which needs recalculation */
+	for (i = 0; i < num_tuples; i++)
+	{
+		int j;
+		bool isnull;
+		SPIPlanPtr plan;
+		SPITupleTable *tuptable_newvals;
+		TupleDesc   tupdesc_newvals;
+
+		/* Set group key values as parameters if needed. */
+		if (keys)
+		{
+			for (j = 0; j < num_keys; j++)
+			{
+				keyVals[j] = SPI_getbinval(tuptable_recalc->vals[i], tupdesc_recalc, j + 2, &isnull);
+				if (isnull)
+					keyNulls[j] = 'n';
+				else
+					keyNulls[j] = ' ';
+			}
+		}
+
+		/*
+		 * Get recalculated values from base tables. The result must be
+		 * only one tuple thich contains the new values for specified keys.
+		 */
+		plan = get_plan_for_recalc(matviewOid, namelist, keys, keyTypes);
+		if (SPI_execute_plan(plan, keyVals, keyNulls, false, 0) != SPI_OK_SELECT)
+			elog(ERROR, "SPI_execute_plan");
+		if (SPI_processed != 1)
+			elog(ERROR, "SPI_execute_plan returned zero or more than one rows");
+
+		tuptable_newvals = SPI_tuptable;
+		tupdesc_newvals = tuptable_newvals->tupdesc;
+
+		Assert(tupdesc_newvals->natts == num_vals);
+
+		/* Set the new values as parameters */
+		for (j = 0; j < tupdesc_newvals->natts; j++)
+		{
+			if (i == 0)
+				types[j] = TupleDescAttr(tupdesc_newvals, j)->atttypid;
+
+			vals[j] = SPI_getbinval(tuptable_newvals->vals[0], tupdesc_newvals, j + 1, &isnull);
+			if (isnull)
+				nulls[j] = 'n';
+			else
+				nulls[j] = ' ';
+		}
+		/* Set TID of the view tuple to be updated as a parameter */
+		types[j] = TIDOID;
+		vals[j] = SPI_getbinval(tuptable_recalc->vals[i], tupdesc_recalc, 1, &isnull);
+		nulls[j] = ' ';
+
+		/* Update the view tuple to the new values */
+		plan = get_plan_for_set_values(matviewOid, matviewname, namelist, types);
+		if (SPI_execute_plan(plan, vals, nulls, false, 0) != SPI_OK_UPDATE)
+			elog(ERROR, "SPI_execute_plan");
+	}
+}
+
+
+/*
+ * get_plan_for_recalc
+ *
+ * Create or fetch a plan for recalculating value in the view's target list
+ * from base tables using the definition query of materialized view specified
+ * by matviewOid. namelist is a list of resnames of values to be recalculated.
+ *
+ * keys is a list of keys to identify tuples to be recalculated if this is not
+ * empty. KeyTypes is an array of types of keys.
+ */
+static SPIPlanPtr
+get_plan_for_recalc(Oid matviewOid, List *namelist, List *keys, Oid *keyTypes)
+{
+	MV_QueryKey hash_key;
+	SPIPlanPtr	plan;
+
+	/* Fetch or prepare a saved plan for the recalculation */
+	mv_BuildQueryKey(&hash_key, matviewOid, MV_PLAN_RECALC);
+	if ((plan = mv_FetchPreparedPlan(&hash_key)) == NULL)
+	{
+		ListCell	   *lc;
+		StringInfoData	str;
+		char   *viewdef;
+
+		/* get view definition of matview */
+		viewdef = text_to_cstring((text *) DatumGetPointer(
+					DirectFunctionCall1(pg_get_viewdef, ObjectIdGetDatum(matviewOid))));
+		/* get rid of trailing semi-colon */
+		viewdef[strlen(viewdef)-1] = '\0';
+
+		/*
+		 * Build a query string for recalculating values. This is like
+		 *
+		 *  SELECT x1, x2, x3, ... FROM ( ... view definition query ...) mv
+		 *   WHERE (key1, key2, ...) = ($1, $2, ...);
+		 */
+
+		initStringInfo(&str);
+		appendStringInfo(&str, "SELECT ");
+		foreach (lc, namelist)
+		{
+			appendStringInfo(&str, "%s", (char *) lfirst(lc));
+			if (lnext(namelist, lc))
+				appendStringInfoString(&str, ", ");
+		}
+		appendStringInfo(&str, " FROM (%s) mv", viewdef);
+
+		if (keys)
+		{
+			int		i = 1;
+			char	paramname[16];
+
+			appendStringInfo(&str, " WHERE (");
+			foreach (lc, keys)
+			{
+				Form_pg_attribute attr = (Form_pg_attribute) lfirst(lc);
+				char   *resname = NameStr(attr->attname);
+				Oid		typid = attr->atttypid;
+
+				sprintf(paramname, "$%d", i);
+				appendStringInfo(&str, "(");
+				generate_equal(&str, typid, resname, paramname);
+				appendStringInfo(&str, " OR (%s IS NULL AND %s IS NULL))",
+								 resname, paramname);
+
+				if (lnext(keys, lc))
+					appendStringInfoString(&str, " AND ");
+				i++;
+			}
+			appendStringInfo(&str, ")");
+		}
+		else
+			keyTypes = NULL;
+
+		plan = SPI_prepare(str.data, list_length(keys), keyTypes);
+		if (plan == NULL)
+			elog(ERROR, "SPI_prepare returned %s for %s", SPI_result_code_string(SPI_result), str.data);
+
+		SPI_keepplan(plan);
+		mv_HashPreparedPlan(&hash_key, plan);
+	}
+
+	return plan;
+}
+
+/*
+ * get_plan_for_set_values
+ *
+ * Create or fetch a plan for applying new values calculated by
+ * get_plan_for_recalc to a materialized view specified by matviewOid.
+ * matviewname is the name of the view.  namelist is a list of resnames
+ * of attributes to be updated, and valTypes is an array of types of the
+ * values.
+ */
+static SPIPlanPtr
+get_plan_for_set_values(Oid matviewOid, char *matviewname, List *namelist,
+						Oid *valTypes)
+{
+	MV_QueryKey	key;
+	SPIPlanPtr	plan;
+
+	/* Fetch or prepare a saved plan for the real check */
+	mv_BuildQueryKey(&key, matviewOid, MV_PLAN_SET_VALUE);
+	if ((plan = mv_FetchPreparedPlan(&key)) == NULL)
+	{
+		ListCell	  *lc;
+		StringInfoData str;
+		int		i;
+
+		/*
+		 * Build a query string for applying min/max values. This is like
+		 *
+		 *  UPDATE matviewname AS mv
+		 *   SET (x1, x2, x3, x4) = ($1, $2, $3, $4)
+		 *   WHERE ctid = $5;
+		 */
+
+		initStringInfo(&str);
+		appendStringInfo(&str, "UPDATE %s AS mv SET (", matviewname);
+		foreach (lc, namelist)
+		{
+			appendStringInfo(&str, "%s", (char *) lfirst(lc));
+			if (lnext(namelist, lc))
+				appendStringInfoString(&str, ", ");
+		}
+		appendStringInfo(&str, ") = ROW(");
+
+		for (i = 1; i <= list_length(namelist); i++)
+			appendStringInfo(&str, "%s$%d", (i==1 ? "" : ", "), i);
+
+		appendStringInfo(&str, ") WHERE ctid OPERATOR(pg_catalog.=) $%d", i);
+
+		plan = SPI_prepare(str.data, list_length(namelist) + 1, valTypes);
+		if (plan == NULL)
+			elog(ERROR, "SPI_prepare returned %s for %s", SPI_result_code_string(SPI_result), str.data);
+
+		SPI_keepplan(plan);
+		mv_HashPreparedPlan(&key, plan);
+	}
+
+	return plan;
+}
+
 /*
  * generate_equals
  *
@@ -2806,6 +3322,13 @@ mv_InitHashTables(void)
 {
 	HASHCTL		ctl;
 
+	memset(&ctl, 0, sizeof(ctl));
+	ctl.keysize = sizeof(MV_QueryKey);
+	ctl.entrysize = sizeof(MV_QueryHashEntry);
+	mv_query_cache = hash_create("MV query cache",
+								 MV_INIT_QUERYHASHSIZE,
+								 &ctl, HASH_ELEM | HASH_BLOBS);
+
 	memset(&ctl, 0, sizeof(ctl));
 	ctl.keysize = sizeof(Oid);
 	ctl.entrysize = sizeof(MV_TriggerHashEntry);
@@ -2814,6 +3337,109 @@ mv_InitHashTables(void)
 								 &ctl, HASH_ELEM | HASH_BLOBS);
 }
 
+/*
+ * mv_FetchPreparedPlan
+ */
+static SPIPlanPtr
+mv_FetchPreparedPlan(MV_QueryKey *key)
+{
+	MV_QueryHashEntry *entry;
+	SPIPlanPtr	plan;
+
+	/*
+	 * On the first call initialize the hashtable
+	 */
+	if (!mv_query_cache)
+		mv_InitHashTables();
+
+	/*
+	 * Lookup for the key
+	 */
+	entry = (MV_QueryHashEntry *) hash_search(mv_query_cache,
+											  (void *) key,
+											  HASH_FIND, NULL);
+	if (entry == NULL)
+		return NULL;
+
+	/*
+	 * Check whether the plan is still valid.  If it isn't, we don't want to
+	 * simply rely on plancache.c to regenerate it; rather we should start
+	 * from scratch and rebuild the query text too.  This is to cover cases
+	 * such as table/column renames.  We depend on the plancache machinery to
+	 * detect possible invalidations, though.
+	 *
+	 * CAUTION: this check is only trustworthy if the caller has already
+	 * locked both materialized views and base tables.
+	 *
+	 * Also, check whether the search_path is still the same as when we made it.
+	 * If it isn't, we need to rebuild the query text because the result of
+	 * pg_ivm_get_viewdef() will change.
+	 */
+	plan = entry->plan;
+	if (plan && SPI_plan_is_valid(plan) &&
+		SearchPathMatchesCurrentEnvironment(entry->search_path))
+		return plan;
+
+	/*
+	 * Otherwise we might as well flush the cached plan now, to free a little
+	 * memory space before we make a new one.
+	 */
+	if (plan)
+		SPI_freeplan(plan);
+	if (entry->search_path)
+		pfree(entry->search_path);
+
+	entry->plan = NULL;
+	entry->search_path = NULL;
+
+	return NULL;
+}
+
+/*
+ * mv_HashPreparedPlan
+ *
+ * Add another plan to our private SPI query plan hashtable.
+ */
+static void
+mv_HashPreparedPlan(MV_QueryKey *key, SPIPlanPtr plan)
+{
+	MV_QueryHashEntry *entry;
+	bool		found;
+
+	/*
+	 * On the first call initialize the hashtable
+	 */
+	if (!mv_query_cache)
+		mv_InitHashTables();
+
+	/*
+	 * Add the new plan.  We might be overwriting an entry previously found
+	 * invalid by mv_FetchPreparedPlan.
+	 */
+	entry = (MV_QueryHashEntry *) hash_search(mv_query_cache,
+											  (void *) key,
+											  HASH_ENTER, &found);
+	Assert(!found || entry->plan == NULL);
+	entry->plan = plan;
+	entry->search_path = GetSearchPathMatcher(TopMemoryContext);
+}
+
+/*
+ * mv_BuildQueryKey
+ *
+ * Construct a hashtable key for a prepared SPI plan for IVM.
+ */
+static void
+mv_BuildQueryKey(MV_QueryKey *key, Oid matview_id, int32 query_type)
+{
+	/*
+	 * We assume struct MV_QueryKey contains no padding bytes, else we'd need
+	 * to use memset to clear them.
+	 */
+	key->matview_id = matview_id;
+	key->query_type = query_type;
+}
+
 /*
  * AtAbort_IVM
  *
-- 
2.25.1


--Multipart=_Sun__31_Mar_2024_22_59_31_+0900_msknEviJj08_wgqO
Content-Type: text/x-diff;
 name="v32-0010-Add-regression-tests-for-Incremental-View-Mainte.patch"
Content-Disposition: attachment;
 filename="v32-0010-Add-regression-tests-for-Incremental-View-Mainte.patch"
Content-Transfer-Encoding: 7bit



^ permalink  raw  reply  [nested|flat] 21+ messages in thread


end of thread, other threads:[~2023-05-31 11:58 UTC | newest]

Thread overview: 21+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]>
2023-05-31 11:58 [PATCH v32 09/11] Add support for min/max aggregates for IVM Yugo Nagata <[email protected]>

This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox