agora 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 v7 2/3] Handle default tablespace in AlterTableInternal
@ 2025-08-04 22:05 Erik Wienhold <[email protected]>
  0 siblings, 0 replies; 21+ messages in thread

From: Erik Wienhold @ 2025-08-04 22:05 UTC (permalink / raw)

Move handling of default tablespace for CREATE OR REPLACE MATERIALIZED
VIEW from create_ctas_internal to ATPrepSetTableSpace.  It feels cleaner
that way in my opinion by not having to resolve the tablespace name just
to pass it to AlterTableInternal.  The default table space is passed as
empty string to AlterTableInternal.
---
 src/backend/commands/createas.c  | 21 ++-------------------
 src/backend/commands/tablecmds.c | 14 ++++++++++++--
 2 files changed, 14 insertions(+), 21 deletions(-)

diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index 1620273f965..30ca0a21903 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -24,7 +24,6 @@
  */
 #include "postgres.h"
 
-#include "miscadmin.h"
 #include "access/heapam.h"
 #include "access/reloptions.h"
 #include "access/tableam.h"
@@ -35,7 +34,6 @@
 #include "commands/matview.h"
 #include "commands/prepare.h"
 #include "commands/tablecmds.h"
-#include "commands/tablespace.h"
 #include "commands/view.h"
 #include "executor/execdesc.h"
 #include "executor/executor.h"
@@ -160,23 +158,8 @@ create_ctas_internal(List *attrList, IntoClause *into)
 		/* tablespace */
 		atcmd = makeNode(AlterTableCmd);
 		atcmd->subtype = AT_SetTableSpace;
-		if (into->tableSpaceName != NULL)
-			atcmd->name = into->tableSpaceName;
-		else
-		{
-			Oid spcid;
-
-			/*
-			 * Resolve the name of the default or database tablespace because
-			 * we need to specify the tablespace by name.
-			 *
-			 * TODO: Move that to ATPrepSetTableSpace? Must allow AlterTableCmd.name to be NULL then.
-			 */
-			spcid = GetDefaultTablespace(RELPERSISTENCE_PERMANENT, false);
-			if (!OidIsValid(spcid))
-				spcid = MyDatabaseTableSpace;
-			atcmd->name = get_tablespace_name(spcid);
-		}
+		/* use empty string to specify default tablespace */
+		atcmd->name = into->tableSpaceName ? into->tableSpaceName : "";
 		atcmds = lappend(atcmds, atcmd);
 
 		/* storage options */
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 44dcd2c5b0d..6ec0b87f841 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -16580,8 +16580,18 @@ ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacen
 {
 	Oid			tablespaceId;
 
-	/* Check that the tablespace exists */
-	tablespaceId = get_tablespace_oid(tablespacename, false);
+	if (tablespacename != NULL && tablespacename[0] == '\0')
+	{
+		/* Use default tablespace if name is empty string */
+		tablespaceId = GetDefaultTablespace(rel->rd_rel->relpersistence, rel->rd_rel->relispartition);
+		if (!OidIsValid(tablespaceId))
+			tablespaceId = MyDatabaseTableSpace;
+	}
+	else
+	{
+		/* Check that the tablespace exists */
+		tablespaceId = get_tablespace_oid(tablespacename, false);
+	}
 
 	/* Check permissions except when moving to database's default */
 	if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
-- 
2.50.1


--r33ycpluvyfavkwy
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment;
	filename="v7-0003-Add-WITH-OLD-DATA-to-CREATE-OR-REPLACE-MATERIALIZ.patch"



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


end of thread, other threads:[~2025-08-04 22:05 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]>
2025-08-04 22:05 [PATCH v7 2/3] Handle default tablespace in AlterTableInternal Erik Wienhold <[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