diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
new file mode 100644
index 110480f..a9dbb87
*** a/src/backend/commands/copy.c
--- b/src/backend/commands/copy.c
*************** BeginCopyFrom(Relation rel,
*** 2296,2302 ****
  			{
  				/* Initialize expressions in copycontext. */
  				defexprs[num_defaults] = ExecInitExpr(
! 								 expression_planner((Expr *) defexpr), NULL);
  				defmap[num_defaults] = attnum - 1;
  				num_defaults++;
  
--- 2296,2304 ----
  			{
  				/* Initialize expressions in copycontext. */
  				defexprs[num_defaults] = ExecInitExpr(
! 										expression_planner((Expr *) defexpr),
! 													  NULL,
! 													  true);
  				defmap[num_defaults] = attnum - 1;
  				num_defaults++;
  
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
new file mode 100644
index 07dc326..5d937fe
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
*************** ATRewriteTable(AlteredTableInfo *tab, Oi
*** 3572,3578 ****
  		NewColumnValue *ex = lfirst(l);
  
  		/* expr already planned */
! 		ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
  	}
  
  	notnull_attrs = NIL;
--- 3572,3578 ----
  		NewColumnValue *ex = lfirst(l);
  
  		/* expr already planned */
! 		ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL, true);
  	}
  
  	notnull_attrs = NIL;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
new file mode 100644
index c6bc6c8..95ebf44
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
*************** GetDomainConstraints(Oid typeOid)
*** 3048,3054 ****
  			r = makeNode(DomainConstraintState);
  			r->constrainttype = DOM_CONSTRAINT_CHECK;
  			r->name = pstrdup(NameStr(c->conname));
! 			r->check_expr = ExecInitExpr(check_expr, NULL);
  
  			/*
  			 * use lcons() here because constraints of lower domains should be
--- 3048,3054 ----
  			r = makeNode(DomainConstraintState);
  			r->constrainttype = DOM_CONSTRAINT_CHECK;
  			r->name = pstrdup(NameStr(c->conname));
! 			r->check_expr = ExecInitExpr(check_expr, NULL, false);
  
  			/*
  			 * use lcons() here because constraints of lower domains should be
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
new file mode 100644
index 4a6baeb..3113f83
*** a/src/backend/executor/execQual.c
--- b/src/backend/executor/execQual.c
***************
*** 51,62 ****
--- 51,70 ----
  #include "pgstat.h"
  #include "utils/acl.h"
  #include "utils/builtins.h"
+ #include "utils/datum.h"
  #include "utils/lsyscache.h"
  #include "utils/memutils.h"
  #include "utils/typcache.h"
  #include "utils/xml.h"
  
  
+ typedef struct
+ {
+ 	PlanState  *parent;
+ 	bool		useCache;
+ } ExecInitExprContext;
+ 
+ 
  /* static function decls */
  static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
  				 ExprContext *econtext,
*************** static Datum ExecEvalCoerceToDomain(Coer
*** 157,162 ****
--- 165,176 ----
  static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate,
  							ExprContext *econtext,
  							bool *isNull, ExprDoneCond *isDone);
+ static Datum ExecEvalCacheExpr(CacheExprState *cstate,
+ 				  ExprContext *econtext,
+ 				  bool *isNull,ExprDoneCond *isDone);
+ static Datum ExecEvalCacheExprResult(CacheExprState *cstate,
+ 						ExprContext *econtext,
+ 						bool *isNull,ExprDoneCond *isDone);
  static Datum ExecEvalFieldSelect(FieldSelectState *fstate,
  					ExprContext *econtext,
  					bool *isNull, ExprDoneCond *isDone);
*************** static Datum ExecEvalArrayCoerceExpr(Arr
*** 174,179 ****
--- 188,195 ----
  						bool *isNull, ExprDoneCond *isDone);
  static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
  					  bool *isNull, ExprDoneCond *isDone);
+ static ExprState *ExecInitExprMutator(Expr *node,
+ 		const ExecInitExprContext *context);
  
  
  /* ----------------------------------------------------------------
*************** ExecEvalBooleanTest(GenericExprState *bs
*** 3754,3759 ****
--- 3770,3832 ----
  	}
  }
  
+ /* ----------------------------------------------------------------
+  *		ExecEvalCacheExpr
+  *
+  * Evaluates a cachable expression for the first time and updates
+  * xprstate.evalfunc to return cached result next time
+  * ----------------------------------------------------------------
+  */
+ static Datum
+ ExecEvalCacheExpr(CacheExprState *cstate, ExprContext *econtext,
+ 				  bool *isNull, ExprDoneCond *isDone)
+ {
+ 	MemoryContext oldcontext;
+ 	Datum		result;
+ 	bool		resultTypByVal;
+ 	int16		resultTypLen;
+ 	Oid			resultType;
+ 
+ 	result = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);
+ 
+ 	if (!cstate->enabled)
+ 		return result;	/* Cache disabled, pass thru the result */
+ 
+ 	/* Set-returning expressions can't be cached */
+ 	Assert(isDone == NULL || *isDone == ExprSingleResult);
+ 
+ 	/* Figure out type and size for copy */
+ 	resultType = exprType((Node *) ((CacheExpr *) cstate->xprstate.expr)->arg);
+ 	get_typlenbyval(resultType, &resultTypLen, &resultTypByVal);
+ 
+ 	/* This cached datum has to persist for the whole query */
+ 	oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+ 	cstate->result = datumCopy(result, resultTypByVal, resultTypLen);
+ 	cstate->isNull = *isNull;
+ 	MemoryContextSwitchTo(oldcontext);
+ 
+ 	/* Subsequent calls will return the cached result */
+ 	cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCacheExprResult;
+ 
+ 	return cstate->result;
+ }
+ 
+ /* ----------------------------------------------------------------
+  *		ExecEvalCacheExprResult
+  *
+  * Return the already-cached result, computed in ExecEvalCacheExpr
+  * ----------------------------------------------------------------
+  */
+ static Datum
+ ExecEvalCacheExprResult(CacheExprState *cstate, ExprContext *econtext,
+ 						bool *isNull, ExprDoneCond *isDone)
+ {
+ 	if (isDone)
+ 		*isDone = ExprSingleResult;
+ 	*isNull = cstate->isNull;
+ 	return cstate->result;
+ }
+ 
  /*
   * ExecEvalCoerceToDomain
   *
*************** ExecEvalExprSwitchContext(ExprState *exp
*** 4217,4229 ****
   *
   *	'node' is the root of the expression tree to examine
   *	'parent' is the PlanState node that owns the expression.
   *
   * 'parent' may be NULL if we are preparing an expression that is not
   * associated with a plan tree.  (If so, it can't have aggs or subplans.)
   * This case should usually come through ExecPrepareExpr, not directly here.
   */
  ExprState *
! ExecInitExpr(Expr *node, PlanState *parent)
  {
  	ExprState  *state;
  
--- 4290,4319 ----
   *
   *	'node' is the root of the expression tree to examine
   *	'parent' is the PlanState node that owns the expression.
+  *	'useCache' enables caching for stable/parameterized expressions.
   *
   * 'parent' may be NULL if we are preparing an expression that is not
   * associated with a plan tree.  (If so, it can't have aggs or subplans.)
   * This case should usually come through ExecPrepareExpr, not directly here.
+  *
+  * 'useCache' may only be true if it's guaranteed that all executions of the
+  * expression use the same snapshot and same external params. It should also
+  * be false if the expression is only executed once.
   */
  ExprState *
! ExecInitExpr(Expr *node, PlanState *parent, bool useCache)
! {
! 	ExecInitExprContext context;
! 
! 	context.parent = parent;
! 	context.useCache = useCache;
! 
! 	return ExecInitExprMutator(node, &context);
! }
! 
! static ExprState *
! ExecInitExprMutator(Expr *node,
! 					const ExecInitExprContext *context)
  {
  	ExprState  *state;
  
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4273,4288 ****
  				AggrefExprState *astate = makeNode(AggrefExprState);
  
  				astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalAggref;
! 				if (parent && IsA(parent, AggState))
  				{
! 					AggState   *aggstate = (AggState *) parent;
  					int			naggs;
  
  					aggstate->aggs = lcons(astate, aggstate->aggs);
  					naggs = ++aggstate->numaggs;
  
! 					astate->args = (List *) ExecInitExpr((Expr *) aggref->args,
! 														 parent);
  
  					/*
  					 * Complain if the aggregate's arguments contain any
--- 4363,4378 ----
  				AggrefExprState *astate = makeNode(AggrefExprState);
  
  				astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalAggref;
! 				if (context->parent && IsA(context->parent, AggState))
  				{
! 					AggState   *aggstate = (AggState *) context->parent;
  					int			naggs;
  
  					aggstate->aggs = lcons(astate, aggstate->aggs);
  					naggs = ++aggstate->numaggs;
  
! 					astate->args = (List *)
! 						ExecInitExprMutator((Expr *) aggref->args, context);
  
  					/*
  					 * Complain if the aggregate's arguments contain any
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4309,4317 ****
  				WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
  
  				wfstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWindowFunc;
! 				if (parent && IsA(parent, WindowAggState))
  				{
! 					WindowAggState *winstate = (WindowAggState *) parent;
  					int			nfuncs;
  
  					winstate->funcs = lcons(wfstate, winstate->funcs);
--- 4399,4407 ----
  				WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
  
  				wfstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWindowFunc;
! 				if (context->parent && IsA(context->parent, WindowAggState))
  				{
! 					WindowAggState *winstate = (WindowAggState *) context->parent;
  					int			nfuncs;
  
  					winstate->funcs = lcons(wfstate, winstate->funcs);
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4319,4326 ****
  					if (wfunc->winagg)
  						winstate->numaggs++;
  
! 					wfstate->args = (List *) ExecInitExpr((Expr *) wfunc->args,
! 														  parent);
  
  					/*
  					 * Complain if the windowfunc's arguments contain any
--- 4409,4416 ----
  					if (wfunc->winagg)
  						winstate->numaggs++;
  
! 					wfstate->args = (List *)
! 						ExecInitExprMutator((Expr *) wfunc->args, context);
  
  					/*
  					 * Complain if the windowfunc's arguments contain any
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4348,4359 ****
  
  				astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayRef;
  				astate->refupperindexpr = (List *)
! 					ExecInitExpr((Expr *) aref->refupperindexpr, parent);
  				astate->reflowerindexpr = (List *)
! 					ExecInitExpr((Expr *) aref->reflowerindexpr, parent);
! 				astate->refexpr = ExecInitExpr(aref->refexpr, parent);
! 				astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr,
! 													parent);
  				/* do one-time catalog lookups for type info */
  				astate->refattrlength = get_typlen(aref->refarraytype);
  				get_typlenbyvalalign(aref->refelemtype,
--- 4438,4449 ----
  
  				astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayRef;
  				astate->refupperindexpr = (List *)
! 					ExecInitExprMutator((Expr *) aref->refupperindexpr, context);
  				astate->reflowerindexpr = (List *)
! 					ExecInitExprMutator((Expr *) aref->reflowerindexpr, context);
! 				astate->refexpr = ExecInitExprMutator(aref->refexpr, context);
! 				astate->refassgnexpr =
! 					ExecInitExprMutator(aref->refassgnexpr, context);
  				/* do one-time catalog lookups for type info */
  				astate->refattrlength = get_typlen(aref->refarraytype);
  				get_typlenbyvalalign(aref->refelemtype,
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4370,4376 ****
  
  				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFunc;
  				fstate->args = (List *)
! 					ExecInitExpr((Expr *) funcexpr->args, parent);
  				fstate->func.fn_oid = InvalidOid;		/* not initialized */
  				state = (ExprState *) fstate;
  			}
--- 4460,4466 ----
  
  				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFunc;
  				fstate->args = (List *)
! 					ExecInitExprMutator((Expr *) funcexpr->args, context);
  				fstate->func.fn_oid = InvalidOid;		/* not initialized */
  				state = (ExprState *) fstate;
  			}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4382,4388 ****
  
  				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalOper;
  				fstate->args = (List *)
! 					ExecInitExpr((Expr *) opexpr->args, parent);
  				fstate->func.fn_oid = InvalidOid;		/* not initialized */
  				state = (ExprState *) fstate;
  			}
--- 4472,4478 ----
  
  				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalOper;
  				fstate->args = (List *)
! 					ExecInitExprMutator((Expr *) opexpr->args, context);
  				fstate->func.fn_oid = InvalidOid;		/* not initialized */
  				state = (ExprState *) fstate;
  			}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4394,4400 ****
  
  				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalDistinct;
  				fstate->args = (List *)
! 					ExecInitExpr((Expr *) distinctexpr->args, parent);
  				fstate->func.fn_oid = InvalidOid;		/* not initialized */
  				state = (ExprState *) fstate;
  			}
--- 4484,4490 ----
  
  				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalDistinct;
  				fstate->args = (List *)
! 					ExecInitExprMutator((Expr *) distinctexpr->args, context);
  				fstate->func.fn_oid = InvalidOid;		/* not initialized */
  				state = (ExprState *) fstate;
  			}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4406,4412 ****
  
  				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullIf;
  				fstate->args = (List *)
! 					ExecInitExpr((Expr *) nullifexpr->args, parent);
  				fstate->func.fn_oid = InvalidOid;		/* not initialized */
  				state = (ExprState *) fstate;
  			}
--- 4496,4502 ----
  
  				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullIf;
  				fstate->args = (List *)
! 					ExecInitExprMutator((Expr *) nullifexpr->args, context);
  				fstate->func.fn_oid = InvalidOid;		/* not initialized */
  				state = (ExprState *) fstate;
  			}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4418,4424 ****
  
  				sstate->fxprstate.xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalScalarArrayOp;
  				sstate->fxprstate.args = (List *)
! 					ExecInitExpr((Expr *) opexpr->args, parent);
  				sstate->fxprstate.func.fn_oid = InvalidOid;		/* not initialized */
  				sstate->element_type = InvalidOid;		/* ditto */
  				state = (ExprState *) sstate;
--- 4508,4514 ----
  
  				sstate->fxprstate.xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalScalarArrayOp;
  				sstate->fxprstate.args = (List *)
! 					ExecInitExprMutator((Expr *) opexpr->args, context);
  				sstate->fxprstate.func.fn_oid = InvalidOid;		/* not initialized */
  				sstate->element_type = InvalidOid;		/* ditto */
  				state = (ExprState *) sstate;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4446,4452 ****
  						break;
  				}
  				bstate->args = (List *)
! 					ExecInitExpr((Expr *) boolexpr->args, parent);
  				state = (ExprState *) bstate;
  			}
  			break;
--- 4536,4542 ----
  						break;
  				}
  				bstate->args = (List *)
! 					ExecInitExprMutator((Expr *) boolexpr->args, context);
  				state = (ExprState *) bstate;
  			}
  			break;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4455,4467 ****
  				SubPlan    *subplan = (SubPlan *) node;
  				SubPlanState *sstate;
  
! 				if (!parent)
  					elog(ERROR, "SubPlan found with no parent plan");
  
! 				sstate = ExecInitSubPlan(subplan, parent);
  
  				/* Add SubPlanState nodes to parent->subPlan */
! 				parent->subPlan = lappend(parent->subPlan, sstate);
  
  				state = (ExprState *) sstate;
  			}
--- 4545,4558 ----
  				SubPlan    *subplan = (SubPlan *) node;
  				SubPlanState *sstate;
  
! 				if (!context->parent)
  					elog(ERROR, "SubPlan found with no parent plan");
  
! 				sstate = ExecInitSubPlan(subplan, context->parent);
  
  				/* Add SubPlanState nodes to parent->subPlan */
! 				context->parent->subPlan = lappend(context->parent->subPlan,
! 												   sstate);
  
  				state = (ExprState *) sstate;
  			}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4471,4480 ****
  				AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
  				AlternativeSubPlanState *asstate;
  
! 				if (!parent)
  					elog(ERROR, "AlternativeSubPlan found with no parent plan");
  
! 				asstate = ExecInitAlternativeSubPlan(asplan, parent);
  
  				state = (ExprState *) asstate;
  			}
--- 4562,4571 ----
  				AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
  				AlternativeSubPlanState *asstate;
  
! 				if (!context->parent)
  					elog(ERROR, "AlternativeSubPlan found with no parent plan");
  
! 				asstate = ExecInitAlternativeSubPlan(asplan, context->parent);
  
  				state = (ExprState *) asstate;
  			}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4485,4491 ****
  				FieldSelectState *fstate = makeNode(FieldSelectState);
  
  				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect;
! 				fstate->arg = ExecInitExpr(fselect->arg, parent);
  				fstate->argdesc = NULL;
  				state = (ExprState *) fstate;
  			}
--- 4576,4582 ----
  				FieldSelectState *fstate = makeNode(FieldSelectState);
  
  				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect;
! 				fstate->arg = ExecInitExprMutator(fselect->arg, context);
  				fstate->argdesc = NULL;
  				state = (ExprState *) fstate;
  			}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4496,4503 ****
  				FieldStoreState *fstate = makeNode(FieldStoreState);
  
  				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldStore;
! 				fstate->arg = ExecInitExpr(fstore->arg, parent);
! 				fstate->newvals = (List *) ExecInitExpr((Expr *) fstore->newvals, parent);
  				fstate->argdesc = NULL;
  				state = (ExprState *) fstate;
  			}
--- 4587,4595 ----
  				FieldStoreState *fstate = makeNode(FieldStoreState);
  
  				fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldStore;
! 				fstate->arg = ExecInitExprMutator(fstore->arg, context);
! 				fstate->newvals = (List *)
! 					ExecInitExprMutator((Expr *) fstore->newvals, context);
  				fstate->argdesc = NULL;
  				state = (ExprState *) fstate;
  			}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4508,4514 ****
  				GenericExprState *gstate = makeNode(GenericExprState);
  
  				gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRelabelType;
! 				gstate->arg = ExecInitExpr(relabel->arg, parent);
  				state = (ExprState *) gstate;
  			}
  			break;
--- 4600,4606 ----
  				GenericExprState *gstate = makeNode(GenericExprState);
  
  				gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRelabelType;
! 				gstate->arg = ExecInitExprMutator(relabel->arg, context);
  				state = (ExprState *) gstate;
  			}
  			break;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4520,4526 ****
  				bool		typisvarlena;
  
  				iostate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceViaIO;
! 				iostate->arg = ExecInitExpr(iocoerce->arg, parent);
  				/* lookup the result type's input function */
  				getTypeInputInfo(iocoerce->resulttype, &iofunc,
  								 &iostate->intypioparam);
--- 4612,4618 ----
  				bool		typisvarlena;
  
  				iostate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceViaIO;
! 				iostate->arg = ExecInitExprMutator(iocoerce->arg, context);
  				/* lookup the result type's input function */
  				getTypeInputInfo(iocoerce->resulttype, &iofunc,
  								 &iostate->intypioparam);
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4538,4544 ****
  				ArrayCoerceExprState *astate = makeNode(ArrayCoerceExprState);
  
  				astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayCoerceExpr;
! 				astate->arg = ExecInitExpr(acoerce->arg, parent);
  				astate->resultelemtype = get_element_type(acoerce->resulttype);
  				if (astate->resultelemtype == InvalidOid)
  					ereport(ERROR,
--- 4630,4636 ----
  				ArrayCoerceExprState *astate = makeNode(ArrayCoerceExprState);
  
  				astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayCoerceExpr;
! 				astate->arg = ExecInitExprMutator(acoerce->arg, context);
  				astate->resultelemtype = get_element_type(acoerce->resulttype);
  				if (astate->resultelemtype == InvalidOid)
  					ereport(ERROR,
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4558,4564 ****
  				ConvertRowtypeExprState *cstate = makeNode(ConvertRowtypeExprState);
  
  				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
! 				cstate->arg = ExecInitExpr(convert->arg, parent);
  				state = (ExprState *) cstate;
  			}
  			break;
--- 4650,4656 ----
  				ConvertRowtypeExprState *cstate = makeNode(ConvertRowtypeExprState);
  
  				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
! 				cstate->arg = ExecInitExprMutator(convert->arg, context);
  				state = (ExprState *) cstate;
  			}
  			break;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4570,4576 ****
  				ListCell   *l;
  
  				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCase;
! 				cstate->arg = ExecInitExpr(caseexpr->arg, parent);
  				foreach(l, caseexpr->args)
  				{
  					CaseWhen   *when = (CaseWhen *) lfirst(l);
--- 4662,4668 ----
  				ListCell   *l;
  
  				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCase;
! 				cstate->arg = ExecInitExprMutator(caseexpr->arg, context);
  				foreach(l, caseexpr->args)
  				{
  					CaseWhen   *when = (CaseWhen *) lfirst(l);
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4579,4590 ****
  					Assert(IsA(when, CaseWhen));
  					wstate->xprstate.evalfunc = NULL;	/* not used */
  					wstate->xprstate.expr = (Expr *) when;
! 					wstate->expr = ExecInitExpr(when->expr, parent);
! 					wstate->result = ExecInitExpr(when->result, parent);
  					outlist = lappend(outlist, wstate);
  				}
  				cstate->args = outlist;
! 				cstate->defresult = ExecInitExpr(caseexpr->defresult, parent);
  				state = (ExprState *) cstate;
  			}
  			break;
--- 4671,4683 ----
  					Assert(IsA(when, CaseWhen));
  					wstate->xprstate.evalfunc = NULL;	/* not used */
  					wstate->xprstate.expr = (Expr *) when;
! 					wstate->expr = ExecInitExprMutator(when->expr, context);
! 					wstate->result = ExecInitExprMutator(when->result, context);
  					outlist = lappend(outlist, wstate);
  				}
  				cstate->args = outlist;
! 				cstate->defresult =
! 					ExecInitExprMutator(caseexpr->defresult, context);
  				state = (ExprState *) cstate;
  			}
  			break;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4601,4607 ****
  					Expr	   *e = (Expr *) lfirst(l);
  					ExprState  *estate;
  
! 					estate = ExecInitExpr(e, parent);
  					outlist = lappend(outlist, estate);
  				}
  				astate->elements = outlist;
--- 4694,4700 ----
  					Expr	   *e = (Expr *) lfirst(l);
  					ExprState  *estate;
  
! 					estate = ExecInitExprMutator(e, context);
  					outlist = lappend(outlist, estate);
  				}
  				astate->elements = outlist;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4669,4675 ****
  						 */
  						e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid);
  					}
! 					estate = ExecInitExpr(e, parent);
  					outlist = lappend(outlist, estate);
  					i++;
  				}
--- 4762,4768 ----
  						 */
  						e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid);
  					}
! 					estate = ExecInitExprMutator(e, context);
  					outlist = lappend(outlist, estate);
  					i++;
  				}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4696,4702 ****
  					Expr	   *e = (Expr *) lfirst(l);
  					ExprState  *estate;
  
! 					estate = ExecInitExpr(e, parent);
  					outlist = lappend(outlist, estate);
  				}
  				rstate->largs = outlist;
--- 4789,4795 ----
  					Expr	   *e = (Expr *) lfirst(l);
  					ExprState  *estate;
  
! 					estate = ExecInitExprMutator(e, context);
  					outlist = lappend(outlist, estate);
  				}
  				rstate->largs = outlist;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4707,4713 ****
  					Expr	   *e = (Expr *) lfirst(l);
  					ExprState  *estate;
  
! 					estate = ExecInitExpr(e, parent);
  					outlist = lappend(outlist, estate);
  				}
  				rstate->rargs = outlist;
--- 4800,4806 ----
  					Expr	   *e = (Expr *) lfirst(l);
  					ExprState  *estate;
  
! 					estate = ExecInitExprMutator(e, context);
  					outlist = lappend(outlist, estate);
  				}
  				rstate->rargs = outlist;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4760,4766 ****
  					Expr	   *e = (Expr *) lfirst(l);
  					ExprState  *estate;
  
! 					estate = ExecInitExpr(e, parent);
  					outlist = lappend(outlist, estate);
  				}
  				cstate->args = outlist;
--- 4853,4859 ----
  					Expr	   *e = (Expr *) lfirst(l);
  					ExprState  *estate;
  
! 					estate = ExecInitExprMutator(e, context);
  					outlist = lappend(outlist, estate);
  				}
  				cstate->args = outlist;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4781,4787 ****
  					Expr	   *e = (Expr *) lfirst(l);
  					ExprState  *estate;
  
! 					estate = ExecInitExpr(e, parent);
  					outlist = lappend(outlist, estate);
  				}
  				mstate->args = outlist;
--- 4874,4880 ----
  					Expr	   *e = (Expr *) lfirst(l);
  					ExprState  *estate;
  
! 					estate = ExecInitExprMutator(e, context);
  					outlist = lappend(outlist, estate);
  				}
  				mstate->args = outlist;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4818,4824 ****
  					Expr	   *e = (Expr *) lfirst(arg);
  					ExprState  *estate;
  
! 					estate = ExecInitExpr(e, parent);
  					outlist = lappend(outlist, estate);
  				}
  				xstate->named_args = outlist;
--- 4911,4917 ----
  					Expr	   *e = (Expr *) lfirst(arg);
  					ExprState  *estate;
  
! 					estate = ExecInitExprMutator(e, context);
  					outlist = lappend(outlist, estate);
  				}
  				xstate->named_args = outlist;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4829,4835 ****
  					Expr	   *e = (Expr *) lfirst(arg);
  					ExprState  *estate;
  
! 					estate = ExecInitExpr(e, parent);
  					outlist = lappend(outlist, estate);
  				}
  				xstate->args = outlist;
--- 4922,4928 ----
  					Expr	   *e = (Expr *) lfirst(arg);
  					ExprState  *estate;
  
! 					estate = ExecInitExprMutator(e, context);
  					outlist = lappend(outlist, estate);
  				}
  				xstate->args = outlist;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4843,4849 ****
  				NullTestState *nstate = makeNode(NullTestState);
  
  				nstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullTest;
! 				nstate->arg = ExecInitExpr(ntest->arg, parent);
  				nstate->argdesc = NULL;
  				state = (ExprState *) nstate;
  			}
--- 4936,4942 ----
  				NullTestState *nstate = makeNode(NullTestState);
  
  				nstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullTest;
! 				nstate->arg = ExecInitExprMutator(ntest->arg, context);
  				nstate->argdesc = NULL;
  				state = (ExprState *) nstate;
  			}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4854,4870 ****
  				GenericExprState *gstate = makeNode(GenericExprState);
  
  				gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalBooleanTest;
! 				gstate->arg = ExecInitExpr(btest->arg, parent);
  				state = (ExprState *) gstate;
  			}
  			break;
  		case T_CoerceToDomain:
  			{
  				CoerceToDomain *ctest = (CoerceToDomain *) node;
  				CoerceToDomainState *cstate = makeNode(CoerceToDomainState);
  
  				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceToDomain;
! 				cstate->arg = ExecInitExpr(ctest->arg, parent);
  				cstate->constraints = GetDomainConstraints(ctest->resulttype);
  				state = (ExprState *) cstate;
  			}
--- 4947,4980 ----
  				GenericExprState *gstate = makeNode(GenericExprState);
  
  				gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalBooleanTest;
! 				gstate->arg = ExecInitExprMutator(btest->arg, context);
  				state = (ExprState *) gstate;
  			}
  			break;
+ 		case T_CacheExpr:
+ 			{
+ 				CacheExpr  *cache = (CacheExpr *) node;
+ 				CacheExprState *cstate = makeNode(CacheExprState);
+ 
+ 				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCacheExpr;
+ 				cstate->arg = ExecInitExprMutator(cache->arg, context);
+ 				/*
+ 				 * If useCache=false, in theory we could simply skip creating
+ 				 * the CacheExprState node in the first place, but that might
+ 				 * be surprising to future developers.
+ 				 */
+ 				cstate->enabled = context->useCache;
+ 
+ 				state = (ExprState *) cstate;
+ 			}
+ 			break;
  		case T_CoerceToDomain:
  			{
  				CoerceToDomain *ctest = (CoerceToDomain *) node;
  				CoerceToDomainState *cstate = makeNode(CoerceToDomainState);
  
  				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceToDomain;
! 				cstate->arg = ExecInitExprMutator(ctest->arg, context);
  				cstate->constraints = GetDomainConstraints(ctest->resulttype);
  				state = (ExprState *) cstate;
  			}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4879,4885 ****
  				GenericExprState *gstate = makeNode(GenericExprState);
  
  				gstate->xprstate.evalfunc = NULL;		/* not used */
! 				gstate->arg = ExecInitExpr(tle->expr, parent);
  				state = (ExprState *) gstate;
  			}
  			break;
--- 4989,4995 ----
  				GenericExprState *gstate = makeNode(GenericExprState);
  
  				gstate->xprstate.evalfunc = NULL;		/* not used */
! 				gstate->arg = ExecInitExprMutator(tle->expr, context);
  				state = (ExprState *) gstate;
  			}
  			break;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4891,4898 ****
  				foreach(l, (List *) node)
  				{
  					outlist = lappend(outlist,
! 									  ExecInitExpr((Expr *) lfirst(l),
! 												   parent));
  				}
  				/* Don't fall through to the "common" code below */
  				return (ExprState *) outlist;
--- 5001,5008 ----
  				foreach(l, (List *) node)
  				{
  					outlist = lappend(outlist,
! 									  ExecInitExprMutator((Expr *) lfirst(l),
! 											  	  	  	  context));
  				}
  				/* Don't fall through to the "common" code below */
  				return (ExprState *) outlist;
*************** ExecPrepareExpr(Expr *node, EState *esta
*** 4931,4937 ****
  
  	node = expression_planner(node);
  
! 	result = ExecInitExpr(node, NULL);
  
  	MemoryContextSwitchTo(oldcontext);
  
--- 5041,5047 ----
  
  	node = expression_planner(node);
  
! 	result = ExecInitExpr(node, NULL, false);
  
  	MemoryContextSwitchTo(oldcontext);
  
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
new file mode 100644
index c9aa921..618a485
*** a/src/backend/executor/nodeAgg.c
--- b/src/backend/executor/nodeAgg.c
*************** ExecInitAgg(Agg *node, EState *estate, i
*** 1444,1453 ****
  	 */
  	aggstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->plan.targetlist,
! 					 (PlanState *) aggstate);
  	aggstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->plan.qual,
! 					 (PlanState *) aggstate);
  
  	/*
  	 * initialize child nodes
--- 1444,1455 ----
  	 */
  	aggstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->plan.targetlist,
! 					 (PlanState *) aggstate,
! 					 true);
  	aggstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->plan.qual,
! 					 (PlanState *) aggstate,
! 					 true);
  
  	/*
  	 * initialize child nodes
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
new file mode 100644
index 849665d..f7e2d1c
*** a/src/backend/executor/nodeBitmapHeapscan.c
--- b/src/backend/executor/nodeBitmapHeapscan.c
*************** ExecInitBitmapHeapScan(BitmapHeapScan *n
*** 569,581 ****
  	 */
  	scanstate->ss.ps.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);
  
  	/*
  	 * tuple table initialization
--- 569,584 ----
  	 */
  	scanstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.targetlist,
! 					 (PlanState *) scanstate,
! 					 true);
  	scanstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.qual,
! 					 (PlanState *) scanstate,
! 					 true);
  	scanstate->bitmapqualorig = (List *)
  		ExecInitExpr((Expr *) node->bitmapqualorig,
! 					 (PlanState *) scanstate,
! 					 true);
  
  	/*
  	 * tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
new file mode 100644
index 7ab15ac..93c2c04
*** a/src/backend/executor/nodeCtescan.c
--- b/src/backend/executor/nodeCtescan.c
*************** ExecInitCteScan(CteScan *node, EState *e
*** 241,250 ****
  	 */
  	scanstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.targetlist,
! 					 (PlanState *) scanstate);
  	scanstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.qual,
! 					 (PlanState *) scanstate);
  
  	/*
  	 * tuple table initialization
--- 241,252 ----
  	 */
  	scanstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.targetlist,
! 					 (PlanState *) scanstate,
! 					 true);
  	scanstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.qual,
! 					 (PlanState *) scanstate,
! 					 true);
  
  	/*
  	 * tuple table initialization
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
new file mode 100644
index 9204859..1602290
*** a/src/backend/executor/nodeForeignscan.c
--- b/src/backend/executor/nodeForeignscan.c
*************** ExecInitForeignScan(ForeignScan *node, E
*** 129,138 ****
  	 */
  	scanstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.targetlist,
! 					 (PlanState *) scanstate);
  	scanstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.qual,
! 					 (PlanState *) scanstate);
  
  	/*
  	 * tuple table initialization
--- 129,140 ----
  	 */
  	scanstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.targetlist,
! 					 (PlanState *) scanstate,
! 					 true);
  	scanstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.qual,
! 					 (PlanState *) scanstate,
! 					 true);
  
  	/*
  	 * tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
new file mode 100644
index a700762..955c4ee
*** a/src/backend/executor/nodeFunctionscan.c
--- b/src/backend/executor/nodeFunctionscan.c
*************** ExecInitFunctionScan(FunctionScan *node,
*** 153,162 ****
  	 */
  	scanstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.targetlist,
! 					 (PlanState *) scanstate);
  	scanstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.qual,
! 					 (PlanState *) scanstate);
  
  	/*
  	 * Now determine if the function returns a simple or composite type, and
--- 153,164 ----
  	 */
  	scanstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.targetlist,
! 					 (PlanState *) scanstate,
! 					 true);
  	scanstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.qual,
! 					 (PlanState *) scanstate,
! 					 true);
  
  	/*
  	 * Now determine if the function returns a simple or composite type, and
*************** ExecInitFunctionScan(FunctionScan *node,
*** 217,223 ****
  	 */
  	scanstate->tuplestorestate = NULL;
  	scanstate->funcexpr = ExecInitExpr((Expr *) node->funcexpr,
! 									   (PlanState *) scanstate);
  
  	scanstate->ss.ps.ps_TupFromTlist = false;
  
--- 219,226 ----
  	 */
  	scanstate->tuplestorestate = NULL;
  	scanstate->funcexpr = ExecInitExpr((Expr *) node->funcexpr,
! 									   (PlanState *) scanstate,
! 									   true);
  
  	scanstate->ss.ps.ps_TupFromTlist = false;
  
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
new file mode 100644
index 80e282b..881e8d7
*** a/src/backend/executor/nodeGroup.c
--- b/src/backend/executor/nodeGroup.c
*************** ExecInitGroup(Group *node, EState *estat
*** 228,237 ****
  	 */
  	grpstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->plan.targetlist,
! 					 (PlanState *) grpstate);
  	grpstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->plan.qual,
! 					 (PlanState *) grpstate);
  
  	/*
  	 * initialize child nodes
--- 228,239 ----
  	 */
  	grpstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->plan.targetlist,
! 					 (PlanState *) grpstate,
! 					 true);
  	grpstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->plan.qual,
! 					 (PlanState *) grpstate,
! 					 true);
  
  	/*
  	 * initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
new file mode 100644
index cabe663..7d65e6d
*** a/src/backend/executor/nodeHash.c
--- b/src/backend/executor/nodeHash.c
*************** ExecInitHash(Hash *node, EState *estate,
*** 179,188 ****
  	 */
  	hashstate->ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->plan.targetlist,
! 					 (PlanState *) hashstate);
  	hashstate->ps.qual = (List *)
  		ExecInitExpr((Expr *) node->plan.qual,
! 					 (PlanState *) hashstate);
  
  	/*
  	 * initialize child nodes
--- 179,190 ----
  	 */
  	hashstate->ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->plan.targetlist,
! 					 (PlanState *) hashstate,
! 					 true);
  	hashstate->ps.qual = (List *)
  		ExecInitExpr((Expr *) node->plan.qual,
! 					 (PlanState *) hashstate,
! 					 true);
  
  	/*
  	 * initialize child nodes
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
new file mode 100644
index 0ea490b..b42ad3f
*** a/src/backend/executor/nodeHashjoin.c
--- b/src/backend/executor/nodeHashjoin.c
*************** ExecInitHashJoin(HashJoin *node, EState
*** 463,479 ****
  	 */
  	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.jointype = node->join.jointype;
  	hjstate->js.joinqual = (List *)
  		ExecInitExpr((Expr *) node->join.joinqual,
! 					 (PlanState *) hjstate);
  	hjstate->hashclauses = (List *)
  		ExecInitExpr((Expr *) node->hashclauses,
! 					 (PlanState *) hjstate);
  
  	/*
  	 * initialize child nodes
--- 463,483 ----
  	 */
  	hjstate->js.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->join.plan.targetlist,
! 					 (PlanState *) hjstate,
! 					 true);
  	hjstate->js.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->join.plan.qual,
! 					 (PlanState *) hjstate,
! 					 true);
  	hjstate->js.jointype = node->join.jointype;
  	hjstate->js.joinqual = (List *)
  		ExecInitExpr((Expr *) node->join.joinqual,
! 					 (PlanState *) hjstate,
! 					 true);
  	hjstate->hashclauses = (List *)
  		ExecInitExpr((Expr *) node->hashclauses,
! 					 (PlanState *) hjstate,
! 					 true);
  
  	/*
  	 * initialize child nodes
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
new file mode 100644
index 4abd805..f20f92f
*** a/src/backend/executor/nodeIndexonlyscan.c
--- b/src/backend/executor/nodeIndexonlyscan.c
*************** ExecInitIndexOnlyScan(IndexOnlyScan *nod
*** 366,378 ****
  	 */
  	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);
  
  	/*
  	 * tuple table initialization
--- 366,381 ----
  	 */
  	indexstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.targetlist,
! 					 (PlanState *) indexstate,
! 					 true);
  	indexstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.qual,
! 					 (PlanState *) indexstate,
! 					 true);
  	indexstate->indexqual = (List *)
  		ExecInitExpr((Expr *) node->indexqual,
! 					 (PlanState *) indexstate,
! 					 true);
  
  	/*
  	 * tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
new file mode 100644
index e577bac..a2c979d
*** a/src/backend/executor/nodeIndexscan.c
--- b/src/backend/executor/nodeIndexscan.c
*************** ExecInitIndexScan(IndexScan *node, EStat
*** 494,506 ****
  	 */
  	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);
  
  	/*
  	 * tuple table initialization
--- 494,509 ----
  	 */
  	indexstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.targetlist,
! 					 (PlanState *) indexstate,
! 					 true);
  	indexstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.qual,
! 					 (PlanState *) indexstate,
! 					 true);
  	indexstate->indexqualorig = (List *)
  		ExecInitExpr((Expr *) node->indexqualorig,
! 					 (PlanState *) indexstate,
! 					 true);
  
  	/*
  	 * tuple table initialization
*************** ExecIndexBuildScanKeys(PlanState *planst
*** 817,823 ****
  				}
  				runtime_keys[n_runtime_keys].scan_key = this_scan_key;
  				runtime_keys[n_runtime_keys].key_expr =
! 					ExecInitExpr(rightop, planstate);
  				runtime_keys[n_runtime_keys].key_toastable =
  					TypeIsToastable(op_righttype);
  				n_runtime_keys++;
--- 820,826 ----
  				}
  				runtime_keys[n_runtime_keys].scan_key = this_scan_key;
  				runtime_keys[n_runtime_keys].key_expr =
! 					ExecInitExpr(rightop, planstate, true);
  				runtime_keys[n_runtime_keys].key_toastable =
  					TypeIsToastable(op_righttype);
  				n_runtime_keys++;
*************** ExecIndexBuildScanKeys(PlanState *planst
*** 944,950 ****
  					}
  					runtime_keys[n_runtime_keys].scan_key = this_sub_key;
  					runtime_keys[n_runtime_keys].key_expr =
! 						ExecInitExpr(rightop, planstate);
  					runtime_keys[n_runtime_keys].key_toastable =
  						TypeIsToastable(op_righttype);
  					n_runtime_keys++;
--- 947,953 ----
  					}
  					runtime_keys[n_runtime_keys].scan_key = this_sub_key;
  					runtime_keys[n_runtime_keys].key_expr =
! 						ExecInitExpr(rightop, planstate, true);
  					runtime_keys[n_runtime_keys].key_toastable =
  						TypeIsToastable(op_righttype);
  					n_runtime_keys++;
*************** ExecIndexBuildScanKeys(PlanState *planst
*** 1062,1068 ****
  					}
  					runtime_keys[n_runtime_keys].scan_key = this_scan_key;
  					runtime_keys[n_runtime_keys].key_expr =
! 						ExecInitExpr(rightop, planstate);
  
  					/*
  					 * Careful here: the runtime expression is not of
--- 1065,1071 ----
  					}
  					runtime_keys[n_runtime_keys].scan_key = this_scan_key;
  					runtime_keys[n_runtime_keys].key_expr =
! 						ExecInitExpr(rightop, planstate, true);
  
  					/*
  					 * Careful here: the runtime expression is not of
*************** ExecIndexBuildScanKeys(PlanState *planst
*** 1080,1086 ****
  				/* Executor has to expand the array value */
  				array_keys[n_array_keys].scan_key = this_scan_key;
  				array_keys[n_array_keys].array_expr =
! 					ExecInitExpr(rightop, planstate);
  				/* the remaining fields were zeroed by palloc0 */
  				n_array_keys++;
  				scanvalue = (Datum) 0;
--- 1083,1089 ----
  				/* Executor has to expand the array value */
  				array_keys[n_array_keys].scan_key = this_scan_key;
  				array_keys[n_array_keys].array_expr =
! 					ExecInitExpr(rightop, planstate, true);
  				/* the remaining fields were zeroed by palloc0 */
  				n_array_keys++;
  				scanvalue = (Datum) 0;
diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c
new file mode 100644
index 8936187..3e2b643
*** a/src/backend/executor/nodeLimit.c
--- b/src/backend/executor/nodeLimit.c
*************** ExecInitLimit(Limit *node, EState *estat
*** 399,407 ****
  	 * initialize child expressions
  	 */
  	limitstate->limitOffset = ExecInitExpr((Expr *) node->limitOffset,
! 										   (PlanState *) limitstate);
  	limitstate->limitCount = ExecInitExpr((Expr *) node->limitCount,
! 										  (PlanState *) limitstate);
  
  	/*
  	 * Tuple table initialization (XXX not actually used...)
--- 399,409 ----
  	 * initialize child expressions
  	 */
  	limitstate->limitOffset = ExecInitExpr((Expr *) node->limitOffset,
! 										   (PlanState *) limitstate,
! 										   true);
  	limitstate->limitCount = ExecInitExpr((Expr *) node->limitCount,
! 										  (PlanState *) limitstate,
! 										  true);
  
  	/*
  	 * Tuple table initialization (XXX not actually used...)
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
new file mode 100644
index f921847..be45c3e
*** a/src/backend/executor/nodeMergejoin.c
--- b/src/backend/executor/nodeMergejoin.c
*************** MJExamineQuals(List *mergeclauses,
*** 206,213 ****
  		/*
  		 * Prepare the input expressions for execution.
  		 */
! 		clause->lexpr = ExecInitExpr((Expr *) linitial(qual->args), parent);
! 		clause->rexpr = ExecInitExpr((Expr *) lsecond(qual->args), parent);
  
  		/* Set up sort support data */
  		clause->ssup.ssup_cxt = CurrentMemoryContext;
--- 206,213 ----
  		/*
  		 * Prepare the input expressions for execution.
  		 */
! 		clause->lexpr = ExecInitExpr((Expr *) linitial(qual->args), parent, true);
! 		clause->rexpr = ExecInitExpr((Expr *) lsecond(qual->args), parent, true);
  
  		/* Set up sort support data */
  		clause->ssup.ssup_cxt = CurrentMemoryContext;
*************** ExecInitMergeJoin(MergeJoin *node, EStat
*** 1498,1511 ****
  	 */
  	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.jointype = node->join.jointype;
  	mergestate->js.joinqual = (List *)
  		ExecInitExpr((Expr *) node->join.joinqual,
! 					 (PlanState *) mergestate);
  	mergestate->mj_ConstFalseJoin = false;
  	/* mergeclauses are handled below */
  
--- 1498,1514 ----
  	 */
  	mergestate->js.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->join.plan.targetlist,
! 					 (PlanState *) mergestate,
! 					 true);
  	mergestate->js.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->join.plan.qual,
! 					 (PlanState *) mergestate,
! 					 true);
  	mergestate->js.jointype = node->join.jointype;
  	mergestate->js.joinqual = (List *)
  		ExecInitExpr((Expr *) node->join.joinqual,
! 					 (PlanState *) mergestate,
! 					 true);
  	mergestate->mj_ConstFalseJoin = false;
  	/* mergeclauses are handled below */
  
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
new file mode 100644
index dfdcb20..3e2810b
*** a/src/backend/executor/nodeModifyTable.c
--- b/src/backend/executor/nodeModifyTable.c
*************** ExecInitModifyTable(ModifyTable *node, E
*** 1003,1009 ****
  			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);
--- 1003,1009 ----
  			List	   *rlist = (List *) lfirst(l);
  			List	   *rliststate;
  
! 			rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps, true);
  			resultRelInfo->ri_projectReturning =
  				ExecBuildProjectionInfo(rliststate, econtext, slot,
  									 resultRelInfo->ri_RelationDesc->rd_att);
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
new file mode 100644
index 11d027b..0452478
*** a/src/backend/executor/nodeNestloop.c
--- b/src/backend/executor/nodeNestloop.c
*************** ExecInitNestLoop(NestLoop *node, EState
*** 322,335 ****
  	 */
  	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.jointype = node->join.jointype;
  	nlstate->js.joinqual = (List *)
  		ExecInitExpr((Expr *) node->join.joinqual,
! 					 (PlanState *) nlstate);
  
  	/*
  	 * initialize child nodes
--- 322,338 ----
  	 */
  	nlstate->js.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->join.plan.targetlist,
! 					 (PlanState *) nlstate,
! 					 true);
  	nlstate->js.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->join.plan.qual,
! 					 (PlanState *) nlstate,
! 					 true);
  	nlstate->js.jointype = node->join.jointype;
  	nlstate->js.joinqual = (List *)
  		ExecInitExpr((Expr *) node->join.joinqual,
! 					 (PlanState *) nlstate,
! 					 true);
  
  	/*
  	 * initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
new file mode 100644
index b51efd8..5e0869c
*** a/src/backend/executor/nodeResult.c
--- b/src/backend/executor/nodeResult.c
*************** ExecInitResult(Result *node, EState *est
*** 240,251 ****
  	 */
  	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);
  
  	/*
  	 * initialize child nodes
--- 240,254 ----
  	 */
  	resstate->ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->plan.targetlist,
! 					 (PlanState *) resstate,
! 					 true);
  	resstate->ps.qual = (List *)
  		ExecInitExpr((Expr *) node->plan.qual,
! 					 (PlanState *) resstate,
! 					 true);
  	resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
! 											 (PlanState *) resstate,
! 											 true);
  
  	/*
  	 * initialize child nodes
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
new file mode 100644
index 9fcd332..17589fa
*** a/src/backend/executor/nodeSeqscan.c
--- b/src/backend/executor/nodeSeqscan.c
*************** ExecInitSeqScan(SeqScan *node, EState *e
*** 182,191 ****
  	 */
  	scanstate->ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->plan.targetlist,
! 					 (PlanState *) scanstate);
  	scanstate->ps.qual = (List *)
  		ExecInitExpr((Expr *) node->plan.qual,
! 					 (PlanState *) scanstate);
  
  	/*
  	 * tuple table initialization
--- 182,193 ----
  	 */
  	scanstate->ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->plan.targetlist,
! 					 (PlanState *) scanstate,
! 					 true);
  	scanstate->ps.qual = (List *)
  		ExecInitExpr((Expr *) node->plan.qual,
! 					 (PlanState *) scanstate,
! 					 true);
  
  	/*
  	 * tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
new file mode 100644
index da31820..b24a372
*** a/src/backend/executor/nodeSubplan.c
--- b/src/backend/executor/nodeSubplan.c
*************** ExecInitSubPlan(SubPlan *subplan, PlanSt
*** 661,668 ****
  											   subplan->plan_id - 1);
  
  	/* Initialize subexpressions */
! 	sstate->testexpr = ExecInitExpr((Expr *) subplan->testexpr, parent);
! 	sstate->args = (List *) ExecInitExpr((Expr *) subplan->args, parent);
  
  	/*
  	 * initialize my state
--- 661,668 ----
  											   subplan->plan_id - 1);
  
  	/* Initialize subexpressions */
! 	sstate->testexpr = ExecInitExpr((Expr *) subplan->testexpr, parent, true);
! 	sstate->args = (List *) ExecInitExpr((Expr *) subplan->args, parent, true);
  
  	/*
  	 * initialize my state
*************** ExecInitAlternativeSubPlan(AlternativeSu
*** 1102,1108 ****
  	 * we're going to use?)
  	 */
  	asstate->subplans = (List *) ExecInitExpr((Expr *) asplan->subplans,
! 											  parent);
  
  	/*
  	 * Select the one to be used.  For this, we need an estimate of the number
--- 1102,1109 ----
  	 * we're going to use?)
  	 */
  	asstate->subplans = (List *) ExecInitExpr((Expr *) asplan->subplans,
! 											  parent,
! 											  true);
  
  	/*
  	 * Select the one to be used.  For this, we need an estimate of the number
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
new file mode 100644
index b5ef152..040ba5c
*** a/src/backend/executor/nodeSubqueryscan.c
--- b/src/backend/executor/nodeSubqueryscan.c
*************** ExecInitSubqueryScan(SubqueryScan *node,
*** 122,131 ****
  	 */
  	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);
  
  	/*
  	 * tuple table initialization
--- 122,133 ----
  	 */
  	subquerystate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.targetlist,
! 					 (PlanState *) subquerystate,
! 					 true);
  	subquerystate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.qual,
! 					 (PlanState *) subquerystate,
! 					 true);
  
  	/*
  	 * tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
new file mode 100644
index 32b8abf..3842911
*** a/src/backend/executor/nodeTidscan.c
--- b/src/backend/executor/nodeTidscan.c
*************** ExecInitTidScan(TidScan *node, EState *e
*** 506,519 ****
  	 */
  	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->tss_tidquals = (List *)
  		ExecInitExpr((Expr *) node->tidquals,
! 					 (PlanState *) tidstate);
  
  	/*
  	 * tuple table initialization
--- 506,522 ----
  	 */
  	tidstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.targetlist,
! 					 (PlanState *) tidstate,
! 					 true);
  	tidstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.qual,
! 					 (PlanState *) tidstate,
! 					 true);
  
  	tidstate->tss_tidquals = (List *)
  		ExecInitExpr((Expr *) node->tidquals,
! 					 (PlanState *) tidstate,
! 					 true);
  
  	/*
  	 * tuple table initialization
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
new file mode 100644
index fc17677..c43ef37
*** a/src/backend/executor/nodeValuesscan.c
--- b/src/backend/executor/nodeValuesscan.c
*************** ValuesNext(ValuesScanState *node)
*** 118,124 ****
  		 * is a SubPlan, and there shouldn't be any (any subselects in the
  		 * VALUES list should be InitPlans).
  		 */
! 		exprstatelist = (List *) ExecInitExpr((Expr *) exprlist, NULL);
  
  		/* parser should have checked all sublists are the same length */
  		Assert(list_length(exprstatelist) == slot->tts_tupleDescriptor->natts);
--- 118,124 ----
  		 * is a SubPlan, and there shouldn't be any (any subselects in the
  		 * VALUES list should be InitPlans).
  		 */
! 		exprstatelist = (List *) ExecInitExpr((Expr *) exprlist, NULL, true);
  
  		/* parser should have checked all sublists are the same length */
  		Assert(list_length(exprstatelist) == slot->tts_tupleDescriptor->natts);
*************** ExecInitValuesScan(ValuesScan *node, ESt
*** 231,240 ****
  	 */
  	scanstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.targetlist,
! 					 (PlanState *) scanstate);
  	scanstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.qual,
! 					 (PlanState *) scanstate);
  
  	/*
  	 * get info about values list
--- 231,242 ----
  	 */
  	scanstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.targetlist,
! 					 (PlanState *) scanstate,
! 					 true);
  	scanstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.qual,
! 					 (PlanState *) scanstate,
! 					 true);
  
  	/*
  	 * get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
new file mode 100644
index ce89ff2..2d2c28b
*** a/src/backend/executor/nodeWindowAgg.c
--- b/src/backend/executor/nodeWindowAgg.c
*************** ExecInitWindowAgg(WindowAgg *node, EStat
*** 1455,1461 ****
  
  	winstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->plan.targetlist,
! 					 (PlanState *) winstate);
  
  	/*
  	 * WindowAgg nodes never have quals, since they can only occur at the
--- 1455,1462 ----
  
  	winstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->plan.targetlist,
! 					 (PlanState *) winstate,
! 					 true);
  
  	/*
  	 * WindowAgg nodes never have quals, since they can only occur at the
*************** ExecInitWindowAgg(WindowAgg *node, EStat
*** 1622,1630 ****
  
  	/* initialize frame bound offset expressions */
  	winstate->startOffset = ExecInitExpr((Expr *) node->startOffset,
! 										 (PlanState *) winstate);
  	winstate->endOffset = ExecInitExpr((Expr *) node->endOffset,
! 									   (PlanState *) winstate);
  
  	winstate->all_first = true;
  	winstate->partition_spooled = false;
--- 1623,1633 ----
  
  	/* initialize frame bound offset expressions */
  	winstate->startOffset = ExecInitExpr((Expr *) node->startOffset,
! 										 (PlanState *) winstate,
! 										 true);
  	winstate->endOffset = ExecInitExpr((Expr *) node->endOffset,
! 									   (PlanState *) winstate,
! 									   true);
  
  	winstate->all_first = true;
  	winstate->partition_spooled = false;
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
new file mode 100644
index e2f3dd4..e68d268
*** a/src/backend/executor/nodeWorktablescan.c
--- b/src/backend/executor/nodeWorktablescan.c
*************** ExecInitWorkTableScan(WorkTableScan *nod
*** 160,169 ****
  	 */
  	scanstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.targetlist,
! 					 (PlanState *) scanstate);
  	scanstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.qual,
! 					 (PlanState *) scanstate);
  
  	/*
  	 * tuple table initialization
--- 160,171 ----
  	 */
  	scanstate->ss.ps.targetlist = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.targetlist,
! 					 (PlanState *) scanstate,
! 					 true);
  	scanstate->ss.ps.qual = (List *)
  		ExecInitExpr((Expr *) node->scan.plan.qual,
! 					 (PlanState *) scanstate,
! 					 true);
  
  	/*
  	 * tuple table initialization
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
new file mode 100644
index cc3168d..8d305b5
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyBooleanTest(const BooleanTest *from
*** 1684,1689 ****
--- 1684,1702 ----
  }
  
  /*
+  * _copyCacheExpr
+  */
+ static CacheExpr *
+ _copyCacheExpr(const CacheExpr *from)
+ {
+ 	CacheExpr *newnode = makeNode(CacheExpr);
+ 
+ 	COPY_NODE_FIELD(arg);
+ 
+ 	return newnode;
+ }
+ 
+ /*
   * _copyCoerceToDomain
   */
  static CoerceToDomain *
*************** copyObject(const void *from)
*** 4010,4015 ****
--- 4023,4031 ----
  		case T_BooleanTest:
  			retval = _copyBooleanTest(from);
  			break;
+ 		case T_CacheExpr:
+ 			retval = _copyCacheExpr(from);
+ 			break;
  		case T_CoerceToDomain:
  			retval = _copyCoerceToDomain(from);
  			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
new file mode 100644
index 2295195..709086a
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalBooleanTest(const BooleanTest *a,
*** 678,683 ****
--- 678,691 ----
  }
  
  static bool
+ _equalCacheExpr(const CacheExpr *a, const CacheExpr *b)
+ {
+ 	COMPARE_NODE_FIELD(arg);
+ 
+ 	return true;
+ }
+ 
+ static bool
  _equalCoerceToDomain(const CoerceToDomain *a, const CoerceToDomain *b)
  {
  	COMPARE_NODE_FIELD(arg);
*************** equal(const void *a, const void *b)
*** 2566,2571 ****
--- 2574,2582 ----
  		case T_BooleanTest:
  			retval = _equalBooleanTest(a, b);
  			break;
+ 		case T_CacheExpr:
+ 			retval = _equalCacheExpr(a, b);
+ 			break;
  		case T_CoerceToDomain:
  			retval = _equalCoerceToDomain(a, b);
  			break;
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
new file mode 100644
index bb5cdae..c8549eb
*** a/src/backend/nodes/makefuncs.c
--- b/src/backend/nodes/makefuncs.c
*************** makeFuncExpr(Oid funcid, Oid rettype, Li
*** 471,476 ****
--- 471,491 ----
  }
  
  /*
+  * makeCacheExpr -
+  *	build an expression node for a cachable expression.
+  */
+ CacheExpr *
+ makeCacheExpr(Expr *arg)
+ {
+ 	CacheExpr  *cacheexpr;
+ 
+ 	cacheexpr = makeNode(CacheExpr);
+ 	cacheexpr->arg = arg;
+ 
+ 	return cacheexpr;
+ }
+ 
+ /*
   * makeDefElem -
   *	build a DefElem node
   *
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
new file mode 100644
index 51459c4..fab2104
*** a/src/backend/nodes/nodeFuncs.c
--- b/src/backend/nodes/nodeFuncs.c
*************** exprType(const Node *expr)
*** 212,217 ****
--- 212,220 ----
  		case T_BooleanTest:
  			type = BOOLOID;
  			break;
+ 		case T_CacheExpr:
+ 			type = exprType((Node *) ((CacheExpr *) expr)->arg);
+ 			break;
  		case T_CoerceToDomain:
  			type = ((const CoerceToDomain *) expr)->resulttype;
  			break;
*************** exprCollation(const Node *expr)
*** 792,797 ****
--- 795,803 ----
  		case T_BooleanTest:
  			coll = InvalidOid;	/* result is always boolean */
  			break;
+ 		case T_CacheExpr:
+ 			coll = exprCollation((Node *) ((CacheExpr *) expr)->arg);
+ 			break;
  		case T_CoerceToDomain:
  			coll = ((const CoerceToDomain *) expr)->resultcollid;
  			break;
*************** exprLocation(const Node *expr)
*** 1263,1268 ****
--- 1269,1278 ----
  			/* just use argument's location */
  			loc = exprLocation((Node *) ((const BooleanTest *) expr)->arg);
  			break;
+ 		case T_CacheExpr:
+ 			/* original expression location */
+ 			loc = exprLocation((Node *) ((CacheExpr *) expr)->arg);
+ 			break;
  		case T_CoerceToDomain:
  			{
  				const CoerceToDomain *cexpr = (const CoerceToDomain *) expr;
*************** expression_tree_walker(Node *node,
*** 1722,1727 ****
--- 1732,1739 ----
  			return walker(((NullTest *) node)->arg, context);
  		case T_BooleanTest:
  			return walker(((BooleanTest *) node)->arg, context);
+ 		case T_CacheExpr:
+ 			return walker(((CacheExpr *) node)->arg, context);
  		case T_CoerceToDomain:
  			return walker(((CoerceToDomain *) node)->arg, context);
  		case T_TargetEntry:
*************** expression_tree_mutator(Node *node,
*** 2374,2379 ****
--- 2386,2401 ----
  				return (Node *) newnode;
  			}
  			break;
+ 		case T_CacheExpr:
+ 			{
+ 				CacheExpr *cache = (CacheExpr *) node;
+ 				CacheExpr *newnode;
+ 
+ 				FLATCOPY(newnode, cache, CacheExpr);
+ 				MUTATE(newnode->arg, cache->arg, Expr *);
+ 				return (Node *) newnode;
+ 			}
+ 			break;
  		case T_CoerceToDomain:
  			{
  				CoerceToDomain *ctest = (CoerceToDomain *) node;
*************** bool
*** 2781,2786 ****
--- 2803,2810 ----
  			return walker(((NullTest *) node)->arg, context);
  		case T_BooleanTest:
  			return walker(((BooleanTest *) node)->arg, context);
+ 		case T_CacheExpr:
+ 			return walker(((CacheExpr *) node)->arg, context);
  		case T_JoinExpr:
  			{
  				JoinExpr   *join = (JoinExpr *) node;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
new file mode 100644
index 829f6d4..b7e6d68
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outBooleanTest(StringInfo str, const Bo
*** 1367,1372 ****
--- 1367,1380 ----
  }
  
  static void
+ _outCacheExpr(StringInfo str, const CacheExpr *node)
+ {
+ 	WRITE_NODE_TYPE("CACHEEXPR");
+ 
+ 	WRITE_NODE_FIELD(arg);
+ }
+ 
+ static void
  _outCoerceToDomain(StringInfo str, const CoerceToDomain *node)
  {
  	WRITE_NODE_TYPE("COERCETODOMAIN");
*************** _outNode(StringInfo str, const void *obj
*** 2909,2914 ****
--- 2917,2925 ----
  			case T_BooleanTest:
  				_outBooleanTest(str, obj);
  				break;
+ 			case T_CacheExpr:
+ 				_outCacheExpr(str, obj);
+ 				break;
  			case T_CoerceToDomain:
  				_outCoerceToDomain(str, obj);
  				break;
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
new file mode 100644
index 885d855..59514b8
*** a/src/backend/optimizer/path/costsize.c
--- b/src/backend/optimizer/path/costsize.c
*************** cost_qual_eval_walker(Node *node, cost_q
*** 3036,3041 ****
--- 3036,3059 ----
  		 */
  		return false;			/* don't recurse into children */
  	}
+ 	else if (IsA(node, CacheExpr))
+ 	{
+ 		cost_qual_eval_context locContext;
+ 		CacheExpr  *cache = (CacheExpr *) node;
+ 
+ 		locContext.root = context->root;
+ 		locContext.total.startup = 0;
+ 		locContext.total.per_tuple = 0;
+ 
+ 		cost_qual_eval_walker((Node *) cache->arg, &locContext);
+ 
+ 		/* Account all cached per-tuple costs once in the startup cost. */
+ 		context->total.startup +=
+ 			locContext.total.startup + locContext.total.per_tuple;
+ 
+ 		/* do NOT recurse into children */
+ 		return false;
+ 	}
  	else if (IsA(node, CoerceViaIO))
  	{
  		CoerceViaIO *iocoerce = (CoerceViaIO *) node;
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
new file mode 100644
index 126d494..1f6dd22
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
*************** static Relids find_nonnullable_rels_walk
*** 97,139 ****
  static List *find_nonnullable_vars_walker(Node *node, bool top_level);
  static bool is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK);
  static bool set_coercionform_dontcare_walker(Node *node, void *context);
! static Node *eval_const_expressions_mutator(Node *node,
! 							   eval_const_expressions_context *context);
  static List *simplify_or_arguments(List *args,
  					  eval_const_expressions_context *context,
! 					  bool *haveNull, bool *forceTrue);
  static List *simplify_and_arguments(List *args,
  					   eval_const_expressions_context *context,
! 					   bool *haveNull, bool *forceFalse);
  static Node *simplify_boolean_equality(Oid opno, List *args);
  static Expr *simplify_function(Expr *oldexpr, Oid funcid,
  				  Oid result_type, int32 result_typmod, Oid result_collid,
  				  Oid input_collid, List **args,
- 				  bool has_named_args,
  				  bool allow_inline,
! 				  eval_const_expressions_context *context);
  static List *reorder_function_arguments(List *args, Oid result_type,
! 						   HeapTuple func_tuple,
! 						   eval_const_expressions_context *context);
  static List *add_function_defaults(List *args, Oid result_type,
! 					  HeapTuple func_tuple,
! 					  eval_const_expressions_context *context);
  static List *fetch_function_defaults(HeapTuple func_tuple);
  static void recheck_cast_function_args(List *args, Oid result_type,
  						   HeapTuple func_tuple);
  static Expr *evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
  				  Oid result_collid, Oid input_collid, List *args,
  				  HeapTuple func_tuple,
! 				  eval_const_expressions_context *context);
  static Expr *inline_function(Oid funcid, Oid result_type, Oid result_collid,
  				Oid input_collid, List *args,
  				HeapTuple func_tuple,
! 				eval_const_expressions_context *context);
  static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
  							 int *usecounts);
  static Node *substitute_actual_parameters_mutator(Node *node,
  							  substitute_actual_parameters_context *context);
  static void sql_inline_error_callback(void *arg);
  static Expr *evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
  			  Oid result_collation);
  static Query *substitute_actual_srf_parameters(Query *expr,
--- 97,146 ----
  static List *find_nonnullable_vars_walker(Node *node, bool top_level);
  static bool is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK);
  static bool set_coercionform_dontcare_walker(Node *node, void *context);
! static Node *caching_const_expressions_mutator(Node *node,
! 								  eval_const_expressions_context *context);
! static Node *const_expressions_mutator(Node *node,
! 						  eval_const_expressions_context *context,
! 						  bool *cachable);
  static List *simplify_or_arguments(List *args,
  					  eval_const_expressions_context *context,
! 					  bool *haveNull, bool *forceTrue, bool *cachable);
  static List *simplify_and_arguments(List *args,
  					   eval_const_expressions_context *context,
! 					   bool *haveNull, bool *forceFalse, bool *cachable);
  static Node *simplify_boolean_equality(Oid opno, List *args);
  static Expr *simplify_function(Expr *oldexpr, Oid funcid,
  				  Oid result_type, int32 result_typmod, Oid result_collid,
  				  Oid input_collid, List **args,
  				  bool allow_inline,
! 				  eval_const_expressions_context *context,
! 				  bool *cachable);
! static List *simplify_copy_function_arguments(List *old_args, Oid result_type,
! 								 HeapTuple func_tuple);
  static List *reorder_function_arguments(List *args, Oid result_type,
! 						   HeapTuple func_tuple);
  static List *add_function_defaults(List *args, Oid result_type,
! 					  HeapTuple func_tuple);
  static List *fetch_function_defaults(HeapTuple func_tuple);
  static void recheck_cast_function_args(List *args, Oid result_type,
  						   HeapTuple func_tuple);
  static Expr *evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
  				  Oid result_collid, Oid input_collid, List *args,
  				  HeapTuple func_tuple,
! 				  eval_const_expressions_context *context,
! 				  bool *cachable);
  static Expr *inline_function(Oid funcid, Oid result_type, Oid result_collid,
  				Oid input_collid, List *args,
  				HeapTuple func_tuple,
! 				eval_const_expressions_context *context,
! 				bool *cachable);
  static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
  							 int *usecounts);
  static Node *substitute_actual_parameters_mutator(Node *node,
  							  substitute_actual_parameters_context *context);
  static void sql_inline_error_callback(void *arg);
+ static inline bool is_cache_useful(Expr *expr);
+ static Expr *insert_cache(Expr *expr);
  static Expr *evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
  			  Oid result_collation);
  static Query *substitute_actual_srf_parameters(Query *expr,
*************** rowtype_field_matches(Oid rowtypeid, int
*** 2028,2033 ****
--- 2035,2045 ----
   * assumption in the presence of user-defined functions; do we need a
   * pg_proc flag that prevents discarding the execution of a function?)
   *
+  * We also insert CacheExpr nodes above expressions that cannot be
+  * evaluated at planning time, but are constant at execution time.
+  * This includes expressions that contain stable function calls and
+  * Param references.
+  *
   * We do understand that certain functions may deliver non-constant
   * results even with constant inputs, "nextval()" being the classic
   * example.  Functions that are not marked "immutable" in pg_proc
*************** eval_const_expressions(PlannerInfo *root
*** 2066,2072 ****
  	context.active_fns = NIL;	/* nothing being recursively simplified */
  	context.case_val = NULL;	/* no CASE being examined */
  	context.estimate = false;	/* safe transformations only */
! 	return eval_const_expressions_mutator(node, &context);
  }
  
  /*--------------------
--- 2078,2085 ----
  	context.active_fns = NIL;	/* nothing being recursively simplified */
  	context.case_val = NULL;	/* no CASE being examined */
  	context.estimate = false;	/* safe transformations only */
! 
! 	return caching_const_expressions_mutator(node, &context);
  }
  
  /*--------------------
*************** eval_const_expressions(PlannerInfo *root
*** 2084,2095 ****
--- 2097,2115 ----
   *	  value of the Param.
   * 2. Fold stable, as well as immutable, functions to constants.
   * 3. Reduce PlaceHolderVar nodes to their contained expressions.
+  * 4. Strip CacheExpr nodes, as selectivity estimation functions aren't
+  *    prepared to deal with CacheExpr nodes.
+  *
+  * NOTE: This function is not indempotent. Calling it twice over an expression
+  * tree causes CacheExpr nodes to be removed in the first pass, then re-added
+  * in the 2nd pass. Make sure it only gets called once.
   *--------------------
   */
  Node *
  estimate_expression_value(PlannerInfo *root, Node *node)
  {
  	eval_const_expressions_context context;
+ 	bool		isCachable = true;
  
  	context.boundParams = root->glob->boundParams;		/* bound Params */
  	/* we do not need to mark the plan as depending on inlined functions */
*************** estimate_expression_value(PlannerInfo *r
*** 2097,2109 ****
  	context.active_fns = NIL;	/* nothing being recursively simplified */
  	context.case_val = NULL;	/* no CASE being examined */
  	context.estimate = true;	/* unsafe transformations OK */
! 	return eval_const_expressions_mutator(node, &context);
  }
  
  static Node *
! eval_const_expressions_mutator(Node *node,
! 							   eval_const_expressions_context *context)
  {
  	if (node == NULL)
  		return NULL;
  	switch (nodeTag(node))
--- 2117,2159 ----
  	context.active_fns = NIL;	/* nothing being recursively simplified */
  	context.case_val = NULL;	/* no CASE being examined */
  	context.estimate = true;	/* unsafe transformations OK */
! 
! 	return const_expressions_mutator(node, &context, &isCachable);
  }
  
+ /*
+  * Calls const_expressions_mutator on the expression tree and automatically
+  * adds a CacheExpr node if the expression is cachable.
+  */
  static Node *
! caching_const_expressions_mutator(Node *node,
! 								  eval_const_expressions_context *context)
  {
+ 	bool		isCachable = true;
+ 
+ 	if (node == NULL)
+ 		return NULL;
+ 
+ 	node = const_expressions_mutator(node, context, &isCachable);
+ 	if (isCachable)
+ 		node = (Node *) insert_cache((Expr *) node);
+ 
+ 	return node;
+ }
+ 
+ /*
+  * Returns a mutated node tree and determines its cachability.
+  *
+  * The caller must make sure that cachable points to a boolean value that's
+  * initialized to TRUE.
+  */
+ static Node *
+ const_expressions_mutator(Node *node,
+ 						  eval_const_expressions_context *context,
+ 						  bool *cachable)
+ {
+ 	Assert(*cachable == true);
+ 
  	if (node == NULL)
  		return NULL;
  	switch (nodeTag(node))
*************** eval_const_expressions_mutator(Node *nod
*** 2112,2117 ****
--- 2162,2175 ----
  			{
  				Param	   *param = (Param *) node;
  
+ 				/*
+ 				 * Only externally-supplied parameters are stable. Other
+ 				 * params are used for passing changing values within the
+ 				 * executor
+ 				 */
+ 				if (param->paramkind != PARAM_EXTERN)
+ 					*cachable = false;
+ 
  				/* Look to see if we've been given a value for this Param */
  				if (param->paramkind == PARAM_EXTERN &&
  					context->boundParams != NULL &&
*************** eval_const_expressions_mutator(Node *nod
*** 2163,2189 ****
  		case T_FuncExpr:
  			{
  				FuncExpr   *expr = (FuncExpr *) node;
! 				List	   *args;
! 				bool		has_named_args;
  				Expr	   *simple;
  				FuncExpr   *newexpr;
- 				ListCell   *lc;
- 
- 				/*
- 				 * Reduce constants in the FuncExpr's arguments, and check to
- 				 * see if there are any named args.
- 				 */
- 				args = NIL;
- 				has_named_args = false;
- 				foreach(lc, expr->args)
- 				{
- 					Node	   *arg = (Node *) lfirst(lc);
- 
- 					arg = eval_const_expressions_mutator(arg, context);
- 					if (IsA(arg, NamedArgExpr))
- 						has_named_args = true;
- 					args = lappend(args, arg);
- 				}
  
  				/*
  				 * Code for op/func reduction is pretty bulky, so split it out
--- 2221,2229 ----
  		case T_FuncExpr:
  			{
  				FuncExpr   *expr = (FuncExpr *) node;
! 				List	   *args = expr->args;
  				Expr	   *simple;
  				FuncExpr   *newexpr;
  
  				/*
  				 * Code for op/func reduction is pretty bulky, so split it out
*************** eval_const_expressions_mutator(Node *nod
*** 2199,2207 ****
  										   expr->funccollid,
  										   expr->inputcollid,
  										   &args,
- 										   has_named_args,
  										   true,
! 										   context);
  				if (simple)		/* successfully simplified it */
  					return (Node *) simple;
  
--- 2239,2247 ----
  										   expr->funccollid,
  										   expr->inputcollid,
  										   &args,
  										   true,
! 										   context,
! 										   cachable);
  				if (simple)		/* successfully simplified it */
  					return (Node *) simple;
  
*************** eval_const_expressions_mutator(Node *nod
*** 2225,2245 ****
  		case T_OpExpr:
  			{
  				OpExpr	   *expr = (OpExpr *) node;
! 				List	   *args;
  				Expr	   *simple;
  				OpExpr	   *newexpr;
  
  				/*
- 				 * Reduce constants in the OpExpr's arguments.  We know args
- 				 * is either NIL or a List node, so we can call
- 				 * expression_tree_mutator directly rather than recursing to
- 				 * self.
- 				 */
- 				args = (List *) expression_tree_mutator((Node *) expr->args,
- 											  eval_const_expressions_mutator,
- 														(void *) context);
- 
- 				/*
  				 * Need to get OID of underlying function.	Okay to scribble
  				 * on input to this extent.
  				 */
--- 2265,2275 ----
  		case T_OpExpr:
  			{
  				OpExpr	   *expr = (OpExpr *) node;
! 				List	   *args = expr->args;
  				Expr	   *simple;
  				OpExpr	   *newexpr;
  
  				/*
  				 * Need to get OID of underlying function.	Okay to scribble
  				 * on input to this extent.
  				 */
*************** eval_const_expressions_mutator(Node *nod
*** 2255,2261 ****
  										   expr->opcollid,
  										   expr->inputcollid,
  										   &args,
! 										   false, true, context);
  				if (simple)		/* successfully simplified it */
  					return (Node *) simple;
  
--- 2285,2292 ----
  										   expr->opcollid,
  										   expr->inputcollid,
  										   &args,
! 										   true, context,
! 										   cachable);
  				if (simple)		/* successfully simplified it */
  					return (Node *) simple;
  
*************** eval_const_expressions_mutator(Node *nod
*** 2292,2326 ****
  		case T_DistinctExpr:
  			{
  				DistinctExpr *expr = (DistinctExpr *) node;
! 				List	   *args;
! 				ListCell   *arg;
  				bool		has_null_input = false;
  				bool		all_null_input = true;
  				bool		has_nonconst_input = false;
  				Expr	   *simple;
  				DistinctExpr *newexpr;
  
  				/*
! 				 * Reduce constants in the DistinctExpr's arguments.  We know
! 				 * args is either NIL or a List node, so we can call
! 				 * expression_tree_mutator directly rather than recursing to
! 				 * self.
  				 */
! 				args = (List *) expression_tree_mutator((Node *) expr->args,
! 											  eval_const_expressions_mutator,
! 														(void *) context);
  
  				/*
  				 * We must do our own check for NULLs because DistinctExpr has
  				 * different results for NULL input than the underlying
  				 * operator does.
  				 */
! 				foreach(arg, args)
  				{
! 					if (IsA(lfirst(arg), Const))
  					{
! 						has_null_input |= ((Const *) lfirst(arg))->constisnull;
! 						all_null_input &= ((Const *) lfirst(arg))->constisnull;
  					}
  					else
  						has_nonconst_input = true;
--- 2323,2371 ----
  		case T_DistinctExpr:
  			{
  				DistinctExpr *expr = (DistinctExpr *) node;
! 				List	   *args = NIL;
! 				ListCell   *lc;
! 				Node	   *arg;
  				bool		has_null_input = false;
  				bool		all_null_input = true;
  				bool		has_nonconst_input = false;
  				Expr	   *simple;
  				DistinctExpr *newexpr;
+ 				bool		leftCachable = true;
+ 				bool		rightCachable = true;
  
  				/*
! 				 * Reduce constants in the DistinctExpr's arguments
! 				 *
! 				 * Note that simplify_function() might call the mutator
! 				 * function on arguments for a second time. However, this is
! 				 * harmless because it's only called when arguments are
! 				 * constant.
  				 */
! 				Assert(list_length(expr->args) == 2);
! 
! 				arg = const_expressions_mutator(linitial(expr->args),
! 												context,
! 												&leftCachable);
! 				args = lappend(args, arg);
! 
! 				arg = const_expressions_mutator(lsecond(expr->args),
! 												context,
! 												&rightCachable);
! 				args = lappend(args, arg);
  
  				/*
  				 * We must do our own check for NULLs because DistinctExpr has
  				 * different results for NULL input than the underlying
  				 * operator does.
  				 */
! 				foreach(lc, args)
  				{
! 					arg = lfirst(lc);
! 					if (IsA(arg, Const))
  					{
! 						has_null_input |= ((Const *) arg)->constisnull;
! 						all_null_input &= ((Const *) arg)->constisnull;
  					}
  					else
  						has_nonconst_input = true;
*************** eval_const_expressions_mutator(Node *nod
*** 2357,2363 ****
  											   expr->opcollid,
  											   expr->inputcollid,
  											   &args,
! 											   false, false, context);
  					if (simple) /* successfully simplified it */
  					{
  						/*
--- 2402,2410 ----
  											   expr->opcollid,
  											   expr->inputcollid,
  											   &args,
! 											   false, context,
! 											   cachable);
! 
  					if (simple) /* successfully simplified it */
  					{
  						/*
*************** eval_const_expressions_mutator(Node *nod
*** 2372,2377 ****
--- 2419,2450 ----
  						return (Node *) csimple;
  					}
  				}
+ 				else if (!leftCachable || !rightCachable)
+ 				{
+ 					*cachable = false;
+ 				}
+ 				else
+ 				{
+ 					/*
+ 					 * This expression is only cachable if the equality
+ 					 * operator is not volatile.
+ 					 */
+ 					HeapTuple	func_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(expr->opfuncid));
+ 					Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
+ 
+ 					if (funcform->provolatile == PROVOLATILE_VOLATILE)
+ 						*cachable = false;
+ 
+ 					ReleaseSysCache(func_tuple);
+ 				}
+ 
+ 				if (!(*cachable))
+ 				{
+ 					if (leftCachable)
+ 						linitial(args) = insert_cache((Expr *) linitial(args));
+ 					if (rightCachable)
+ 						lsecond(args) = insert_cache((Expr *) lsecond(args));
+ 				}
  
  				/*
  				 * The expression cannot be simplified any further, so build
*************** eval_const_expressions_mutator(Node *nod
*** 2404,2410 ****
  							newargs = simplify_or_arguments(expr->args,
  															context,
  															&haveNull,
! 															&forceTrue);
  							if (forceTrue)
  								return makeBoolConst(true, false);
  							if (haveNull)
--- 2477,2484 ----
  							newargs = simplify_or_arguments(expr->args,
  															context,
  															&haveNull,
! 															&forceTrue,
! 															cachable);
  							if (forceTrue)
  								return makeBoolConst(true, false);
  							if (haveNull)
*************** eval_const_expressions_mutator(Node *nod
*** 2432,2438 ****
  							newargs = simplify_and_arguments(expr->args,
  															 context,
  															 &haveNull,
! 															 &forceFalse);
  							if (forceFalse)
  								return makeBoolConst(false, false);
  							if (haveNull)
--- 2506,2513 ----
  							newargs = simplify_and_arguments(expr->args,
  															 context,
  															 &haveNull,
! 															 &forceFalse,
! 															 cachable);
  							if (forceFalse)
  								return makeBoolConst(false, false);
  							if (haveNull)
*************** eval_const_expressions_mutator(Node *nod
*** 2456,2463 ****
  							Node	   *arg;
  
  							Assert(list_length(expr->args) == 1);
! 							arg = eval_const_expressions_mutator(linitial(expr->args),
! 																 context);
  
  							/*
  							 * Use negate_clause() to see if we can simplify
--- 2531,2539 ----
  							Node	   *arg;
  
  							Assert(list_length(expr->args) == 1);
! 							arg = const_expressions_mutator(linitial(expr->args),
! 															context,
! 															cachable);
  
  							/*
  							 * Use negate_clause() to see if we can simplify
*************** eval_const_expressions_mutator(Node *nod
*** 2481,2486 ****
--- 2557,2563 ----
  			 * XXX should we ereport() here instead?  Probably this routine
  			 * should never be invoked after SubPlan creation.
  			 */
+ 			*cachable = false;
  			return node;
  		case T_RelabelType:
  			{
*************** eval_const_expressions_mutator(Node *nod
*** 2493,2500 ****
  				RelabelType *relabel = (RelabelType *) node;
  				Node	   *arg;
  
! 				arg = eval_const_expressions_mutator((Node *) relabel->arg,
! 													 context);
  
  				/*
  				 * If we find stacked RelabelTypes (eg, from foo :: int ::
--- 2570,2578 ----
  				RelabelType *relabel = (RelabelType *) node;
  				Node	   *arg;
  
! 				arg = const_expressions_mutator((Node *) relabel->arg,
! 												context,
! 												cachable);
  
  				/*
  				 * If we find stacked RelabelTypes (eg, from foo :: int ::
*************** eval_const_expressions_mutator(Node *nod
*** 2528,2534 ****
  		case T_CoerceViaIO:
  			{
  				CoerceViaIO *expr = (CoerceViaIO *) node;
- 				Expr	   *arg;
  				List	   *args;
  				Oid			outfunc;
  				bool		outtypisvarlena;
--- 2606,2611 ----
*************** eval_const_expressions_mutator(Node *nod
*** 2540,2548 ****
  				/*
  				 * Reduce constants in the CoerceViaIO's argument.
  				 */
! 				arg = (Expr *) eval_const_expressions_mutator((Node *) expr->arg,
! 															  context);
! 				args = list_make1(arg);
  
  				/*
  				 * CoerceViaIO represents calling the source type's output
--- 2617,2623 ----
  				/*
  				 * Reduce constants in the CoerceViaIO's argument.
  				 */
! 				args = list_make1(expr->arg);
  
  				/*
  				 * CoerceViaIO represents calling the source type's output
*************** eval_const_expressions_mutator(Node *nod
*** 2553,2559 ****
  				 * Note that the coercion functions are assumed not to care
  				 * about input collation, so we just pass InvalidOid for that.
  				 */
! 				getTypeOutputInfo(exprType((Node *) arg),
  								  &outfunc, &outtypisvarlena);
  				getTypeInputInfo(expr->resulttype,
  								 &infunc, &intypioparam);
--- 2628,2634 ----
  				 * Note that the coercion functions are assumed not to care
  				 * about input collation, so we just pass InvalidOid for that.
  				 */
! 				getTypeOutputInfo(exprType((Node *) expr->arg),
  								  &outfunc, &outtypisvarlena);
  				getTypeInputInfo(expr->resulttype,
  								 &infunc, &intypioparam);
*************** eval_const_expressions_mutator(Node *nod
*** 2564,2570 ****
  										   InvalidOid,
  										   InvalidOid,
  										   &args,
! 										   false, true, context);
  				if (simple)		/* successfully simplified output fn */
  				{
  					/*
--- 2639,2646 ----
  										   InvalidOid,
  										   InvalidOid,
  										   &args,
! 										   true, context,
! 										   cachable);
  				if (simple)		/* successfully simplified output fn */
  				{
  					/*
*************** eval_const_expressions_mutator(Node *nod
*** 2594,2600 ****
  											   expr->resultcollid,
  											   InvalidOid,
  											   &args,
! 											   false, true, context);
  					if (simple) /* successfully simplified input fn */
  						return (Node *) simple;
  				}
--- 2670,2677 ----
  											   expr->resultcollid,
  											   InvalidOid,
  											   &args,
! 											   true, context,
! 											   cachable);
  					if (simple) /* successfully simplified input fn */
  						return (Node *) simple;
  				}
*************** eval_const_expressions_mutator(Node *nod
*** 2605,2611 ****
  				 * possibly-simplified argument.
  				 */
  				newexpr = makeNode(CoerceViaIO);
! 				newexpr->arg = arg;
  				newexpr->resulttype = expr->resulttype;
  				newexpr->resultcollid = expr->resultcollid;
  				newexpr->coerceformat = expr->coerceformat;
--- 2682,2688 ----
  				 * possibly-simplified argument.
  				 */
  				newexpr = makeNode(CoerceViaIO);
! 				newexpr->arg = (Expr *) linitial(args);
  				newexpr->resulttype = expr->resulttype;
  				newexpr->resultcollid = expr->resultcollid;
  				newexpr->coerceformat = expr->coerceformat;
*************** eval_const_expressions_mutator(Node *nod
*** 2622,2629 ****
  				 * Reduce constants in the ArrayCoerceExpr's argument, then
  				 * build a new ArrayCoerceExpr.
  				 */
! 				arg = (Expr *) eval_const_expressions_mutator((Node *) expr->arg,
! 															  context);
  
  				newexpr = makeNode(ArrayCoerceExpr);
  				newexpr->arg = arg;
--- 2699,2707 ----
  				 * Reduce constants in the ArrayCoerceExpr's argument, then
  				 * build a new ArrayCoerceExpr.
  				 */
! 				arg = (Expr *) const_expressions_mutator((Node *) expr->arg,
! 														 context,
! 														 cachable);
  
  				newexpr = makeNode(ArrayCoerceExpr);
  				newexpr->arg = arg;
*************** eval_const_expressions_mutator(Node *nod
*** 2646,2651 ****
--- 2724,2739 ----
  												  newexpr->resulttype,
  												  newexpr->resulttypmod,
  												  newexpr->resultcollid);
+ 				/*
+ 				 * If the argument is cachable, but conversion isn't, insert a
+ 				 * CacheExpr above the argument
+ 				 */
+ 				if (arg && *cachable && (OidIsValid(newexpr->elemfuncid) &&
+ 				 func_volatile(newexpr->elemfuncid) == PROVOLATILE_VOLATILE))
+ 				{
+ 					*cachable = false;
+ 					newexpr->arg = insert_cache(arg);
+ 				}
  
  				/* Else we must return the partially-simplified node */
  				return (Node *) newexpr;
*************** eval_const_expressions_mutator(Node *nod
*** 2663,2670 ****
  				CollateExpr *collate = (CollateExpr *) node;
  				Node	   *arg;
  
! 				arg = eval_const_expressions_mutator((Node *) collate->arg,
! 													 context);
  
  				if (arg && IsA(arg, Const))
  				{
--- 2751,2759 ----
  				CollateExpr *collate = (CollateExpr *) node;
  				Node	   *arg;
  
! 				arg = const_expressions_mutator((Node *) collate->arg,
! 												context,
! 												cachable);
  
  				if (arg && IsA(arg, Const))
  				{
*************** eval_const_expressions_mutator(Node *nod
*** 2733,2745 ****
  				Node	   *save_case_val;
  				Node	   *newarg;
  				List	   *newargs;
  				bool		const_true_cond;
  				Node	   *defresult = NULL;
  				ListCell   *arg;
  
  				/* Simplify the test expression, if any */
! 				newarg = eval_const_expressions_mutator((Node *) caseexpr->arg,
! 														context);
  
  				/* Set up for contained CaseTestExpr nodes */
  				save_case_val = context->case_val;
--- 2822,2835 ----
  				Node	   *save_case_val;
  				Node	   *newarg;
  				List	   *newargs;
+ 				List	   *cachable_args = NIL;
  				bool		const_true_cond;
  				Node	   *defresult = NULL;
  				ListCell   *arg;
  
  				/* Simplify the test expression, if any */
! 				newarg = caching_const_expressions_mutator((Node *) caseexpr->arg,
! 														   context);
  
  				/* Set up for contained CaseTestExpr nodes */
  				save_case_val = context->case_val;
*************** eval_const_expressions_mutator(Node *nod
*** 2759,2770 ****
  					CaseWhen   *oldcasewhen = (CaseWhen *) lfirst(arg);
  					Node	   *casecond;
  					Node	   *caseresult;
  
  					Assert(IsA(oldcasewhen, CaseWhen));
  
  					/* Simplify this alternative's test condition */
! 					casecond = eval_const_expressions_mutator((Node *) oldcasewhen->expr,
! 															  context);
  
  					/*
  					 * If the test condition is constant FALSE (or NULL), then
--- 2849,2863 ----
  					CaseWhen   *oldcasewhen = (CaseWhen *) lfirst(arg);
  					Node	   *casecond;
  					Node	   *caseresult;
+ 					bool		condCachable = true;
+ 					bool		resultCachable = true;
  
  					Assert(IsA(oldcasewhen, CaseWhen));
  
  					/* Simplify this alternative's test condition */
! 					casecond = const_expressions_mutator((Node *) oldcasewhen->expr,
! 														 context,
! 														 &condCachable);
  
  					/*
  					 * If the test condition is constant FALSE (or NULL), then
*************** eval_const_expressions_mutator(Node *nod
*** 2783,2790 ****
  					}
  
  					/* Simplify this alternative's result value */
! 					caseresult = eval_const_expressions_mutator((Node *) oldcasewhen->result,
! 																context);
  
  					/* If non-constant test condition, emit a new WHEN node */
  					if (!const_true_cond)
--- 2876,2884 ----
  					}
  
  					/* Simplify this alternative's result value */
! 					caseresult = const_expressions_mutator((Node *) oldcasewhen->result,
! 														   context,
! 														   &resultCachable);
  
  					/* If non-constant test condition, emit a new WHEN node */
  					if (!const_true_cond)
*************** eval_const_expressions_mutator(Node *nod
*** 2795,2800 ****
--- 2889,2911 ----
  						newcasewhen->result = (Expr *) caseresult;
  						newcasewhen->location = oldcasewhen->location;
  						newargs = lappend(newargs, newcasewhen);
+ 
+ 						if (condCachable)
+ 						{
+ 							if (is_cache_useful((Expr *) casecond))
+ 								cachable_args = lappend(cachable_args, &newcasewhen->expr);
+ 						}
+ 						else
+ 							*cachable = false;
+ 
+ 						if (resultCachable)
+ 						{
+ 							if (is_cache_useful((Expr *) caseresult))
+ 								cachable_args = lappend(cachable_args, &newcasewhen->result);
+ 						}
+ 						else
+ 							*cachable = false;
+ 
  						continue;
  					}
  
*************** eval_const_expressions_mutator(Node *nod
*** 2809,2816 ****
  
  				/* Simplify the default result, unless we replaced it above */
  				if (!const_true_cond)
! 					defresult = eval_const_expressions_mutator((Node *) caseexpr->defresult,
! 															   context);
  
  				context->case_val = save_case_val;
  
--- 2920,2940 ----
  
  				/* Simplify the default result, unless we replaced it above */
  				if (!const_true_cond)
! 				{
! 					bool		isCachable = true;
! 
! 					defresult = const_expressions_mutator((Node *) caseexpr->defresult,
! 														  context,
! 														  &isCachable);
! 
! 					if (isCachable)
! 					{
! 						if (is_cache_useful((Expr *) defresult))
! 							cachable_args = lappend(cachable_args, &defresult);
! 					}
! 					else
! 						*cachable = false;
! 				}
  
  				context->case_val = save_case_val;
  
*************** eval_const_expressions_mutator(Node *nod
*** 2820,2825 ****
--- 2944,2962 ----
  				 */
  				if (newargs == NIL)
  					return defresult;
+ 
+ 				if (!(*cachable))
+ 				{
+ 					ListCell   *lc;
+ 
+ 					foreach(lc, cachable_args)
+ 					{
+ 						Expr	  **arg = (Expr **) lfirst(lc);
+ 
+ 						*arg = (Expr *) makeCacheExpr(*arg);
+ 					}
+ 				}
+ 
  				/* Otherwise we need a new CASE node */
  				newcase = makeNode(CaseExpr);
  				newcase->casetype = caseexpr->casetype;
*************** eval_const_expressions_mutator(Node *nod
*** 2840,2846 ****
--- 2977,2986 ----
  				if (context->case_val)
  					return copyObject(context->case_val);
  				else
+ 				{
+ 					*cachable = false;
  					return copyObject(node);
+ 				}
  			}
  		case T_ArrayExpr:
  			{
*************** eval_const_expressions_mutator(Node *nod
*** 2850,2862 ****
  				List	   *newelems;
  				ListCell   *element;
  
  				newelems = NIL;
  				foreach(element, arrayexpr->elements)
  				{
  					Node	   *e;
  
! 					e = eval_const_expressions_mutator((Node *) lfirst(element),
! 													   context);
  					if (!IsA(e, Const))
  						all_const = false;
  					newelems = lappend(newelems, e);
--- 2990,3004 ----
  				List	   *newelems;
  				ListCell   *element;
  
+ 				*cachable = false;	/* Not implemented */
+ 
  				newelems = NIL;
  				foreach(element, arrayexpr->elements)
  				{
  					Node	   *e;
  
! 					e = caching_const_expressions_mutator((Node *) lfirst(element),
! 														  context);
  					if (!IsA(e, Const))
  						all_const = false;
  					newelems = lappend(newelems, e);
*************** eval_const_expressions_mutator(Node *nod
*** 2883,2897 ****
  				CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
  				CoalesceExpr *newcoalesce;
  				List	   *newargs;
! 				ListCell   *arg;
  
  				newargs = NIL;
! 				foreach(arg, coalesceexpr->args)
  				{
! 					Node	   *e;
  
! 					e = eval_const_expressions_mutator((Node *) lfirst(arg),
! 													   context);
  
  					/*
  					 * We can remove null constants from the list. For a
--- 3025,3042 ----
  				CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
  				CoalesceExpr *newcoalesce;
  				List	   *newargs;
! 				List	   *cachable_args = NIL;
! 				ListCell   *lc;
  
  				newargs = NIL;
! 				foreach(lc, coalesceexpr->args)
  				{
! 					Node	   *arg = lfirst(lc);
! 					bool		isCachable = true;
  
! 					arg = const_expressions_mutator((Node *) arg,
! 													context,
! 													&isCachable);
  
  					/*
  					 * We can remove null constants from the list. For a
*************** eval_const_expressions_mutator(Node *nod
*** 2901,2916 ****
  					 * drop following arguments since they will never be
  					 * reached.
  					 */
! 					if (IsA(e, Const))
  					{
! 						if (((Const *) e)->constisnull)
  							continue;	/* drop null constant */
  						if (newargs == NIL)
! 							return e;	/* first expr */
! 						newargs = lappend(newargs, e);
  						break;
  					}
! 					newargs = lappend(newargs, e);
  				}
  
  				/*
--- 3046,3070 ----
  					 * drop following arguments since they will never be
  					 * reached.
  					 */
! 					if (IsA(arg, Const))
  					{
! 						if (((Const *) arg)->constisnull)
  							continue;	/* drop null constant */
  						if (newargs == NIL)
! 							return arg; /* first expr */
! 						newargs = lappend(newargs, arg);
  						break;
  					}
! 
! 					newargs = lappend(newargs, arg);
! 
! 					if (isCachable)
! 					{
! 						if (is_cache_useful((Expr *) arg))
! 							cachable_args = lappend(cachable_args, &llast(newargs));
! 					}
! 					else
! 						*cachable = false;
  				}
  
  				/*
*************** eval_const_expressions_mutator(Node *nod
*** 2921,2926 ****
--- 3075,3089 ----
  					return (Node *) makeNullConst(coalesceexpr->coalescetype,
  												  -1,
  											   coalesceexpr->coalescecollid);
+ 				if (!(*cachable))
+ 				{
+ 					foreach(lc, cachable_args)
+ 					{
+ 						Expr	  **arg = (Expr **) lfirst(lc);
+ 
+ 						*arg = (Expr *) makeCacheExpr(*arg);
+ 					}
+ 				}
  
  				newcoalesce = makeNode(CoalesceExpr);
  				newcoalesce->coalescetype = coalesceexpr->coalescetype;
*************** eval_const_expressions_mutator(Node *nod
*** 2942,2954 ****
  				 * is still the same as when the FieldSelect was created ---
  				 * this can change if someone did ALTER COLUMN TYPE on the
  				 * rowtype.
  				 */
  				FieldSelect *fselect = (FieldSelect *) node;
  				FieldSelect *newfselect;
  				Node	   *arg;
  
! 				arg = eval_const_expressions_mutator((Node *) fselect->arg,
! 													 context);
  				if (arg && IsA(arg, Var) &&
  					((Var *) arg)->varattno == InvalidAttrNumber)
  				{
--- 3105,3122 ----
  				 * is still the same as when the FieldSelect was created ---
  				 * this can change if someone did ALTER COLUMN TYPE on the
  				 * rowtype.
+ 				 *
+ 				 * This is never cachable because Var references aren't
+ 				 * constants. simplify_function() also refuses caching of
+ 				 * row-returning functions
  				 */
  				FieldSelect *fselect = (FieldSelect *) node;
  				FieldSelect *newfselect;
  				Node	   *arg;
  
! 				arg = const_expressions_mutator((Node *) fselect->arg,
! 												context,
! 												cachable);
  				if (arg && IsA(arg, Var) &&
  					((Var *) arg)->varattno == InvalidAttrNumber)
  				{
*************** eval_const_expressions_mutator(Node *nod
*** 2999,3006 ****
  				NullTest   *newntest;
  				Node	   *arg;
  
! 				arg = eval_const_expressions_mutator((Node *) ntest->arg,
! 													 context);
  				if (arg && IsA(arg, RowExpr))
  				{
  					/*
--- 3167,3175 ----
  				NullTest   *newntest;
  				Node	   *arg;
  
! 				arg = const_expressions_mutator((Node *) ntest->arg,
! 												context,
! 												cachable);
  				if (arg && IsA(arg, RowExpr))
  				{
  					/*
*************** eval_const_expressions_mutator(Node *nod
*** 3083,3090 ****
  				BooleanTest *newbtest;
  				Node	   *arg;
  
! 				arg = eval_const_expressions_mutator((Node *) btest->arg,
! 													 context);
  				if (arg && IsA(arg, Const))
  				{
  					Const	   *carg = (Const *) arg;
--- 3252,3260 ----
  				BooleanTest *newbtest;
  				Node	   *arg;
  
! 				arg = const_expressions_mutator((Node *) btest->arg,
! 												context,
! 												cachable);
  				if (arg && IsA(arg, Const))
  				{
  					Const	   *carg = (Const *) arg;
*************** eval_const_expressions_mutator(Node *nod
*** 3130,3136 ****
  				return (Node *) newbtest;
  			}
  		case T_PlaceHolderVar:
- 
  			/*
  			 * In estimation mode, just strip the PlaceHolderVar node
  			 * altogether; this amounts to estimating that the contained value
--- 3300,3305 ----
*************** eval_const_expressions_mutator(Node *nod
*** 3138,3152 ****
  			 * just use the default behavior (ie, simplify the expression but
  			 * leave the PlaceHolderVar node intact).
  			 */
  			if (context->estimate)
  			{
  				PlaceHolderVar *phv = (PlaceHolderVar *) node;
  
! 				return eval_const_expressions_mutator((Node *) phv->phexpr,
! 													  context);
  			}
  			break;
  		default:
  			break;
  	}
  
--- 3307,3342 ----
  			 * just use the default behavior (ie, simplify the expression but
  			 * leave the PlaceHolderVar node intact).
  			 */
+ 			*cachable = false;
+ 
  			if (context->estimate)
  			{
  				PlaceHolderVar *phv = (PlaceHolderVar *) node;
+ 				bool isCachable = true;	/* ignored */
  
! 				return const_expressions_mutator((Node *) phv->phexpr,
! 												 context,
! 												 &isCachable);
! 			}
! 			break;
! 		case T_Const:
! 			/* Keep *cachable=true */
! 			break;
! 		case T_CacheExpr:
! 			/* We already have CacheExpr in the appropriate place.
! 			 * In estimation mode, strip CacheExpr nodes here to simplify
! 			 * selectivity estimation functions.
! 			 */
! 			*cachable = false;
! 			if (context->estimate)
! 			{
! 				CacheExpr  *cache = (CacheExpr *) node;
! 				return const_expressions_mutator((Node *) cache->arg, context, cachable);
  			}
  			break;
  		default:
+ 			/* Everything else is not cachable */
+ 			*cachable = false;
  			break;
  	}
  
*************** eval_const_expressions_mutator(Node *nod
*** 3154,3163 ****
  	 * For any node type not handled above, we recurse using
  	 * expression_tree_mutator, which will copy the node unchanged but try to
  	 * simplify its arguments (if any) using this routine. For example: we
! 	 * cannot eliminate an ArrayRef node, but we might be able to simplify
! 	 * constant expressions in its subscripts.
  	 */
! 	return expression_tree_mutator(node, eval_const_expressions_mutator,
  								   (void *) context);
  }
  
--- 3344,3354 ----
  	 * For any node type not handled above, we recurse using
  	 * expression_tree_mutator, which will copy the node unchanged but try to
  	 * simplify its arguments (if any) using this routine. For example: we
! 	 * cannot eliminate an ArrayRef node, but we might be able to simplify or
! 	 * cache constant expressions in its subscripts.
  	 */
! 	return expression_tree_mutator(node,
! 								   caching_const_expressions_mutator,
  								   (void *) context);
  }
  
*************** eval_const_expressions_mutator(Node *nod
*** 3179,3191 ****
   * The output arguments *haveNull and *forceTrue must be initialized FALSE
   * by the caller.  They will be set TRUE if a null constant or true constant,
   * respectively, is detected anywhere in the argument list.
   */
  static List *
  simplify_or_arguments(List *args,
  					  eval_const_expressions_context *context,
! 					  bool *haveNull, bool *forceTrue)
  {
! 	List	   *newargs = NIL;
  	List	   *unprocessed_args;
  
  	/*
--- 3370,3392 ----
   * The output arguments *haveNull and *forceTrue must be initialized FALSE
   * by the caller.  They will be set TRUE if a null constant or true constant,
   * respectively, is detected anywhere in the argument list.
+  *
+  * We divide elements into two separate lists, one for cachable items and one
+  * for non-cachable items. Upon returning, the cachable sub-list is turned
+  * into a new BoolExpr, cached and prepended. This is done in hopes that the
+  * cachable sub-list is faster to evaluate and short-cicruits the rest of the
+  * expression.
+  *
+  * Input:  (cachable OR uncachable OR cachable OR uncachable)
+  * Output: (CACHE(cachable OR cachable) OR uncachable OR uncachable)
   */
  static List *
  simplify_or_arguments(List *args,
  					  eval_const_expressions_context *context,
! 					  bool *haveNull, bool *forceTrue, bool *cachable)
  {
! 	List	   *nocache_args = NIL;
! 	List	   *cachable_args = NIL;
  	List	   *unprocessed_args;
  
  	/*
*************** simplify_or_arguments(List *args,
*** 3200,3205 ****
--- 3401,3407 ----
  	while (unprocessed_args)
  	{
  		Node	   *arg = (Node *) linitial(unprocessed_args);
+ 		bool		isCachable = true;
  
  		unprocessed_args = list_delete_first(unprocessed_args);
  
*************** simplify_or_arguments(List *args,
*** 3222,3228 ****
  		}
  
  		/* If it's not an OR, simplify it */
! 		arg = eval_const_expressions_mutator(arg, context);
  
  		/*
  		 * It is unlikely but not impossible for simplification of a non-OR
--- 3424,3430 ----
  		}
  
  		/* If it's not an OR, simplify it */
! 		arg = const_expressions_mutator(arg, context, &isCachable);
  
  		/*
  		 * It is unlikely but not impossible for simplification of a non-OR
*************** simplify_or_arguments(List *args,
*** 3264,3273 ****
  		}
  
  		/* else emit the simplified arg into the result list */
! 		newargs = lappend(newargs, arg);
  	}
  
! 	return newargs;
  }
  
  /*
--- 3466,3505 ----
  		}
  
  		/* else emit the simplified arg into the result list */
! 		if (isCachable)
! 			cachable_args = lappend(cachable_args, arg);
! 		else
! 			nocache_args = lappend(nocache_args, arg);
  	}
  
! 	if (cachable_args && nocache_args)
! 	{
! 		Expr	   *arg;
! 
! 		/* Build a new expression for cachable sub-list */
! 		if (list_length(cachable_args) == 1)
! 			arg = linitial(cachable_args);
! 		else
! 			arg = makeBoolExpr(OR_EXPR, cachable_args, -1);
! 
! 		arg = insert_cache(arg);
! 
! 		/*
! 		 * Assume that the cachable expression is cheaper to evaluate, so put
! 		 * it first
! 		 */
! 		nocache_args = lcons(arg, nocache_args);
! 
! 		*cachable = false;
! 		return nocache_args;
! 	}
! 	else if (nocache_args)
! 	{
! 		*cachable = false;
! 		return nocache_args;
! 	}
! 	else
! 		return cachable_args;
  }
  
  /*
*************** simplify_or_arguments(List *args,
*** 3288,3300 ****
   * The output arguments *haveNull and *forceFalse must be initialized FALSE
   * by the caller.  They will be set TRUE if a null constant or false constant,
   * respectively, is detected anywhere in the argument list.
   */
  static List *
  simplify_and_arguments(List *args,
  					   eval_const_expressions_context *context,
! 					   bool *haveNull, bool *forceFalse)
  {
! 	List	   *newargs = NIL;
  	List	   *unprocessed_args;
  
  	/* See comments in simplify_or_arguments */
--- 3520,3542 ----
   * The output arguments *haveNull and *forceFalse must be initialized FALSE
   * by the caller.  They will be set TRUE if a null constant or false constant,
   * respectively, is detected anywhere in the argument list.
+  *
+  * We divide elements into two separate lists, one for cachable items and one
+  * for non-cachable items. Upon returning, the cachable sub-list is turned
+  * into a new BoolExpr, cached and prepended. This is done in hopes that the
+  * cachable sub-list is faster to evaluate and short-cicruits the rest of the
+  * expression.
+  *
+  * Input:  (cachable OR uncachable OR cachable OR uncachable)
+  * Output: (CACHE(cachable OR cachable) OR uncachable OR uncachable)
   */
  static List *
  simplify_and_arguments(List *args,
  					   eval_const_expressions_context *context,
! 					   bool *haveNull, bool *forceFalse, bool *cachable)
  {
! 	List	   *nocache_args = NIL;
! 	List	   *cachable_args = NIL;
  	List	   *unprocessed_args;
  
  	/* See comments in simplify_or_arguments */
*************** simplify_and_arguments(List *args,
*** 3302,3307 ****
--- 3544,3550 ----
  	while (unprocessed_args)
  	{
  		Node	   *arg = (Node *) linitial(unprocessed_args);
+ 		bool		isCachable = true;
  
  		unprocessed_args = list_delete_first(unprocessed_args);
  
*************** simplify_and_arguments(List *args,
*** 3324,3330 ****
  		}
  
  		/* If it's not an AND, simplify it */
! 		arg = eval_const_expressions_mutator(arg, context);
  
  		/*
  		 * It is unlikely but not impossible for simplification of a non-AND
--- 3567,3573 ----
  		}
  
  		/* If it's not an AND, simplify it */
! 		arg = const_expressions_mutator(arg, context, &isCachable);
  
  		/*
  		 * It is unlikely but not impossible for simplification of a non-AND
*************** simplify_and_arguments(List *args,
*** 3366,3375 ****
  		}
  
  		/* else emit the simplified arg into the result list */
! 		newargs = lappend(newargs, arg);
  	}
  
! 	return newargs;
  }
  
  /*
--- 3609,3648 ----
  		}
  
  		/* else emit the simplified arg into the result list */
! 		if (isCachable)
! 			cachable_args = lappend(cachable_args, arg);
! 		else
! 			nocache_args = lappend(nocache_args, arg);
  	}
  
! 	if (cachable_args && nocache_args)
! 	{
! 		Expr	   *arg;
! 
! 		/* Build a new expression for cachable sub-list */
! 		if (list_length(cachable_args) == 1)
! 			arg = linitial(cachable_args);
! 		else
! 			arg = makeBoolExpr(AND_EXPR, cachable_args, -1);
! 
! 		arg = insert_cache(arg);
! 
! 		/*
! 		 * Assume that the cachable expression is cheaper to evaluate, so put
! 		 * it first
! 		 */
! 		nocache_args = lcons(arg, nocache_args);
! 
! 		*cachable = false;
! 		return nocache_args;
! 	}
! 	else if (nocache_args)
! 	{
! 		*cachable = false;
! 		return nocache_args;
! 	}
! 	else
! 		return cachable_args;
  }
  
  /*
*************** simplify_boolean_equality(Oid opno, List
*** 3444,3450 ****
   * Inputs are the original expression (can be NULL), function OID, actual
   * result type OID (which is needed for polymorphic functions), result typmod,
   * result collation, the input collation to use for the function, the
!  * pre-simplified argument list, and some flags; also the context data for
   * eval_const_expressions.	In common cases, several of the arguments could be
   * derived from the original expression.  Sending them separately avoids
   * duplicating NodeTag-specific knowledge, and it's necessary for CoerceViaIO.
--- 3717,3723 ----
   * Inputs are the original expression (can be NULL), function OID, actual
   * result type OID (which is needed for polymorphic functions), result typmod,
   * result collation, the input collation to use for the function, the
!  * un-simplified argument list, and some flags; also the context data for
   * eval_const_expressions.	In common cases, several of the arguments could be
   * derived from the original expression.  Sending them separately avoids
   * duplicating NodeTag-specific knowledge, and it's necessary for CoerceViaIO.
*************** simplify_boolean_equality(Oid opno, List
*** 3463,3476 ****
  static Expr *
  simplify_function(Expr *oldexpr, Oid funcid,
  				  Oid result_type, int32 result_typmod, Oid result_collid,
! 				  Oid input_collid, List **args,
! 				  bool has_named_args,
  				  bool allow_inline,
! 				  eval_const_expressions_context *context)
  {
  	HeapTuple	func_tuple;
  	Expr	   *newexpr;
  	Oid			transform;
  
  	/*
  	 * We have three strategies for simplification: execute the function to
--- 3736,3752 ----
  static Expr *
  simplify_function(Expr *oldexpr, Oid funcid,
  				  Oid result_type, int32 result_typmod, Oid result_collid,
! 				  Oid input_collid, List **old_args,
  				  bool allow_inline,
! 				  eval_const_expressions_context *context,
! 				  bool *cachable)
  {
  	HeapTuple	func_tuple;
  	Expr	   *newexpr;
  	Oid			transform;
+ 	ListCell   *lc;
+ 	List	   *args = NIL;
+ 	List	   *cachable_args = NIL;
  
  	/*
  	 * We have three strategies for simplification: execute the function to
*************** simplify_function(Expr *oldexpr, Oid fun
*** 3484,3502 ****
  	if (!HeapTupleIsValid(func_tuple))
  		elog(ERROR, "cache lookup failed for function %u", funcid);
  
  	/*
! 	 * While we have the tuple, reorder named arguments and add default
! 	 * arguments if needed.
  	 */
- 	if (has_named_args)
- 		*args = reorder_function_arguments(*args, result_type, func_tuple,
- 										   context);
- 	else if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
- 		*args = add_function_defaults(*args, result_type, func_tuple, context);
- 
  	newexpr = evaluate_function(funcid, result_type, result_typmod,
! 								result_collid, input_collid, *args,
! 								func_tuple, context);
  
  	/*
  	 * Some functions calls can be simplified at plan time based on properties
--- 3760,3812 ----
  	if (!HeapTupleIsValid(func_tuple))
  		elog(ERROR, "cache lookup failed for function %u", funcid);
  
+ 	if (oldexpr && IsA(oldexpr, FuncExpr))
+ 
+ 		/*
+ 		 * Reorder named arguments and add defaults if needed. Returns a
+ 		 * copied list, so we can mutate it later.
+ 		 */
+ 		args = simplify_copy_function_arguments(*old_args, result_type, func_tuple);
+ 	else
+ 		/* Copy argument list before we start mutating it */
+ 		args = list_copy(*old_args);
+ 
+ 	/* Reduce constants in the FuncExpr's arguments */
+ 	foreach(lc, args)
+ 	{
+ 		Node	   *arg = (Node *) lfirst(lc);
+ 		bool		isCachable = true;
+ 
+ 		arg = const_expressions_mutator(arg, context, &isCachable);
+ 		lfirst(lc) = arg;
+ 
+ 		/*
+ 		 * We're stuck in a catch-22 here. If all arguments and the call
+ 		 * itself is cachable, we don't want to insert cache nodes for
+ 		 * arguments. But we don't know that until we walk through all the
+ 		 * arguments.
+ 		 *
+ 		 * So we accumulate cachable arguments in a list of ListCell pointers,
+ 		 * which we will update later if necessary.
+ 		 *
+ 		 * Note: The args list may not be mutated from here on this until we
+ 		 * handle cachable_args below.
+ 		 */
+ 		if (isCachable)
+ 		{
+ 			if (is_cache_useful((Expr *) arg))
+ 				cachable_args = lappend(cachable_args, &lfirst(lc));
+ 		}
+ 		else
+ 			*cachable = false;	/* One bad arg spoils the whole cache */
+ 	}
+ 
  	/*
! 	 * evaluate_function tells us about the cachability of the function call
  	 */
  	newexpr = evaluate_function(funcid, result_type, result_typmod,
! 								result_collid, input_collid, args,
! 								func_tuple, context, cachable);
  
  	/*
  	 * Some functions calls can be simplified at plan time based on properties
*************** simplify_function(Expr *oldexpr, Oid fun
*** 3533,3562 ****
  												  PointerGetDatum(oldexpr)));
  
  	if (!newexpr && allow_inline)
  		newexpr = inline_function(funcid, result_type, result_collid,
! 								  input_collid, *args,
! 								  func_tuple, context);
  
  	ReleaseSysCache(func_tuple);
  
  	return newexpr;
  }
  
  /*
   * reorder_function_arguments: convert named-notation args to positional args
   *
   * This function also inserts default argument values as needed, since it's
   * impossible to form a truly valid positional call without that.
   */
  static List *
! reorder_function_arguments(List *args, Oid result_type, HeapTuple func_tuple,
! 						   eval_const_expressions_context *context)
  {
  	Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
  	int			pronargs = funcform->pronargs;
  	int			nargsprovided = list_length(args);
  	Node	   *argarray[FUNC_MAX_ARGS];
- 	Bitmapset  *defargnumbers;
  	ListCell   *lc;
  	int			i;
  
--- 3843,3953 ----
  												  PointerGetDatum(oldexpr)));
  
  	if (!newexpr && allow_inline)
+ 	{
+ 		/*
+ 		 * The inlined expression may be cachable regardless of the above, if
+ 		 * the function's volatility was mis-labeled or if volatile parts are
+ 		 * removed (possible due to constant folding of conditionals).
+ 		 *
+ 		 * inline_function() also takes care of caching all cachable subtrees
+ 		 */
+ 		bool		isCachable = true;
+ 
  		newexpr = inline_function(funcid, result_type, result_collid,
! 								  input_collid, args,
! 								  func_tuple, context, &isCachable);
! 
! 		if (newexpr)
! 			*cachable = isCachable;
! 	}
  
  	ReleaseSysCache(func_tuple);
  
+ 	/*
+ 	 * If function call can't be cached/inlined, update all cachable arguments
+ 	 */
+ 	if (!newexpr && !(*cachable))
+ 	{
+ 		foreach(lc, cachable_args)
+ 		{
+ 			Node	  **arg = (Node **) lfirst(lc);
+ 
+ 			*arg = (Node *) makeCacheExpr((Expr *) *arg);
+ 		}
+ 	}
+ 
+ 	/* Argument processing done, give it back to the caller */
+ 	*old_args = args;
+ 
  	return newexpr;
  }
  
  /*
+  * This function prepares a function's argument list -- converting
+  * named-notation argument list into positional notation while adding any
+  * needed default argument expressions.
+  *
+  * Always returns a copy of the argument list, the original list is not
+  * modified.
+  */
+ static List *
+ simplify_copy_function_arguments(List *old_args, Oid result_type,
+ 								 HeapTuple func_tuple)
+ {
+ 	List	   *args = NIL;
+ 	ListCell   *lc;
+ 	bool		has_named_args = false;
+ 	int			nargs_before;
+ 
+ 	/* Do we need to reorder named arguments? */
+ 	foreach(lc, old_args)
+ 	{
+ 		Node	   *arg = (Node *) lfirst(lc);
+ 
+ 		if (IsA(arg, NamedArgExpr))
+ 		{
+ 			has_named_args = true;
+ 			break;
+ 		}
+ 	}
+ 
+ 	nargs_before = list_length(old_args);
+ 
+ 	/*
+ 	 * Reorder named arguments and add default arguments if needed.
+ 	 */
+ 	if (has_named_args)
+ 		args = reorder_function_arguments(old_args, result_type, func_tuple);
+ 
+ 	else
+ 	{
+ 		args = list_copy(old_args);
+ 
+ 		/* Append missing default arguments to the list */
+ 		if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(args))
+ 			args = add_function_defaults(args, result_type, func_tuple);
+ 	}
+ 
+ 	if (list_length(args) != nargs_before)
+ 		/* Added defaults may need casts */
+ 		recheck_cast_function_args(args, result_type, func_tuple);
+ 
+ 	return args;
+ }
+ 
+ /*
   * reorder_function_arguments: convert named-notation args to positional args
   *
   * This function also inserts default argument values as needed, since it's
   * impossible to form a truly valid positional call without that.
   */
  static List *
! reorder_function_arguments(List *args, Oid result_type, HeapTuple func_tuple)
  {
  	Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
  	int			pronargs = funcform->pronargs;
  	int			nargsprovided = list_length(args);
  	Node	   *argarray[FUNC_MAX_ARGS];
  	ListCell   *lc;
  	int			i;
  
*************** reorder_function_arguments(List *args, O
*** 3590,3596 ****
  	 * Fetch default expressions, if needed, and insert into array at proper
  	 * locations (they aren't necessarily consecutive or all used)
  	 */
- 	defargnumbers = NULL;
  	if (nargsprovided < pronargs)
  	{
  		List	   *defaults = fetch_function_defaults(func_tuple);
--- 3981,3986 ----
*************** reorder_function_arguments(List *args, O
*** 3599,3608 ****
  		foreach(lc, defaults)
  		{
  			if (argarray[i] == NULL)
- 			{
  				argarray[i] = (Node *) lfirst(lc);
- 				defargnumbers = bms_add_member(defargnumbers, i);
- 			}
  			i++;
  		}
  	}
--- 3989,3995 ----
*************** reorder_function_arguments(List *args, O
*** 3615,3646 ****
  		args = lappend(args, argarray[i]);
  	}
  
- 	/* Recheck argument types and add casts if needed */
- 	recheck_cast_function_args(args, result_type, func_tuple);
- 
- 	/*
- 	 * Lastly, we have to recursively simplify the defaults we just added (but
- 	 * don't recurse on the args passed in, as we already did those). This
- 	 * isn't merely an optimization, it's *necessary* since there could be
- 	 * functions with named or defaulted arguments down in there.
- 	 *
- 	 * Note that we do this last in hopes of simplifying any typecasts that
- 	 * were added by recheck_cast_function_args --- there shouldn't be any new
- 	 * casts added to the explicit arguments, but casts on the defaults are
- 	 * possible.
- 	 */
- 	if (defargnumbers != NULL)
- 	{
- 		i = 0;
- 		foreach(lc, args)
- 		{
- 			if (bms_is_member(i, defargnumbers))
- 				lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
- 															context);
- 			i++;
- 		}
- 	}
- 
  	return args;
  }
  
--- 4002,4007 ----
*************** reorder_function_arguments(List *args, O
*** 3651,3670 ****
   * and so we know we just need to add defaults at the end.
   */
  static List *
! add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
! 					  eval_const_expressions_context *context)
  {
  	Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
- 	int			nargsprovided = list_length(args);
  	List	   *defaults;
  	int			ndelete;
- 	ListCell   *lc;
  
  	/* Get all the default expressions from the pg_proc tuple */
  	defaults = fetch_function_defaults(func_tuple);
  
  	/* Delete any unused defaults from the list */
! 	ndelete = nargsprovided + list_length(defaults) - funcform->pronargs;
  	if (ndelete < 0)
  		elog(ERROR, "not enough default arguments");
  	while (ndelete-- > 0)
--- 4012,4028 ----
   * and so we know we just need to add defaults at the end.
   */
  static List *
! add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple)
  {
  	Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
  	List	   *defaults;
  	int			ndelete;
  
  	/* Get all the default expressions from the pg_proc tuple */
  	defaults = fetch_function_defaults(func_tuple);
  
  	/* Delete any unused defaults from the list */
! 	ndelete = list_length(args) + list_length(defaults) - funcform->pronargs;
  	if (ndelete < 0)
  		elog(ERROR, "not enough default arguments");
  	while (ndelete-- > 0)
*************** add_function_defaults(List *args, Oid re
*** 3673,3700 ****
  	/* And form the combined argument list */
  	args = list_concat(args, defaults);
  
- 	/* Recheck argument types and add casts if needed */
- 	recheck_cast_function_args(args, result_type, func_tuple);
- 
- 	/*
- 	 * Lastly, we have to recursively simplify the defaults we just added (but
- 	 * don't recurse on the args passed in, as we already did those). This
- 	 * isn't merely an optimization, it's *necessary* since there could be
- 	 * functions with named or defaulted arguments down in there.
- 	 *
- 	 * Note that we do this last in hopes of simplifying any typecasts that
- 	 * were added by recheck_cast_function_args --- there shouldn't be any new
- 	 * casts added to the explicit arguments, but casts on the defaults are
- 	 * possible.
- 	 */
- 	foreach(lc, args)
- 	{
- 		if (nargsprovided-- > 0)
- 			continue;			/* skip original arg positions */
- 		lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
- 													context);
- 	}
- 
  	return args;
  }
  
--- 4031,4036 ----
*************** static Expr *
*** 3784,3790 ****
  evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
  				  Oid result_collid, Oid input_collid, List *args,
  				  HeapTuple func_tuple,
! 				  eval_const_expressions_context *context)
  {
  	Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
  	bool		has_nonconst_input = false;
--- 4120,4127 ----
  evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
  				  Oid result_collid, Oid input_collid, List *args,
  				  HeapTuple func_tuple,
! 				  eval_const_expressions_context *context,
! 				  bool *cachable)
  {
  	Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
  	bool		has_nonconst_input = false;
*************** evaluate_function(Oid funcid, Oid result
*** 3796,3802 ****
--- 4133,4142 ----
  	 * Can't simplify if it returns a set.
  	 */
  	if (funcform->proretset)
+ 	{
+ 		*cachable = false;
  		return NULL;
+ 	}
  
  	/*
  	 * Can't simplify if it returns RECORD.  The immediate problem is that it
*************** evaluate_function(Oid funcid, Oid result
*** 3810,3816 ****
--- 4150,4162 ----
  	 * gotchas, seems best to leave the function call unreduced.
  	 */
  	if (funcform->prorettype == RECORDOID)
+ 	{
+ 		*cachable = false;
  		return NULL;
+ 	}
+ 
+ 	if (funcform->provolatile == PROVOLATILE_VOLATILE)
+ 		*cachable = false;
  
  	/*
  	 * Check for constant inputs and especially constant-NULL inputs.
*************** static Expr *
*** 3907,3913 ****
  inline_function(Oid funcid, Oid result_type, Oid result_collid,
  				Oid input_collid, List *args,
  				HeapTuple func_tuple,
! 				eval_const_expressions_context *context)
  {
  	Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
  	char	   *src;
--- 4253,4260 ----
  inline_function(Oid funcid, Oid result_type, Oid result_collid,
  				Oid input_collid, List *args,
  				HeapTuple func_tuple,
! 				eval_const_expressions_context *context,
! 				bool *cachable)
  {
  	Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
  	char	   *src;
*************** inline_function(Oid funcid, Oid result_t
*** 4186,4192 ****
  	 * the current function to the context list of active functions.
  	 */
  	context->active_fns = lcons_oid(funcid, context->active_fns);
! 	newexpr = eval_const_expressions_mutator(newexpr, context);
  	context->active_fns = list_delete_first(context->active_fns);
  
  	error_context_stack = sqlerrcontext.previous;
--- 4533,4539 ----
  	 * the current function to the context list of active functions.
  	 */
  	context->active_fns = lcons_oid(funcid, context->active_fns);
! 	newexpr = const_expressions_mutator(newexpr, context, cachable);
  	context->active_fns = list_delete_first(context->active_fns);
  
  	error_context_stack = sqlerrcontext.previous;
*************** sql_inline_error_callback(void *arg)
*** 4266,4271 ****
--- 4613,4646 ----
  }
  
  /*
+  * Is it useful to cache this expression? Constants and param references are
+  * always fast to access so don't insert cache in front of those.
+  *
+  * Without inline, we lose almost 10% time in some very simple queries (!)
+  */
+ static inline bool
+ is_cache_useful(Expr *expr)
+ {
+ 	if (IsA(expr, Const))
+ 		return false;
+ 	if (IsA(expr, Param))
+ 		return false;
+ 	return true;
+ }
+ 
+ static Expr *
+ insert_cache(Expr *expr)
+ {
+ 	/* Don't cache obviously cheap expressions */
+ 	if (!is_cache_useful(expr))
+ 		return expr;
+ 
+ 	Assert(!IsA(expr, CacheExpr));
+ 
+ 	return (Expr *) makeCacheExpr(expr);
+ }
+ 
+ /*
   * evaluate_expr: pre-evaluate a constant expression
   *
   * We use the executor's routine ExecEvalExpr() to avoid duplication of
*************** evaluate_expr(Expr *expr, Oid result_typ
*** 4298,4304 ****
  	 * Prepare expr for execution.	(Note: we can't use ExecPrepareExpr
  	 * because it'd result in recursively invoking eval_const_expressions.)
  	 */
! 	exprstate = ExecInitExpr(expr, NULL);
  
  	/*
  	 * And evaluate it.
--- 4673,4679 ----
  	 * Prepare expr for execution.	(Note: we can't use ExecPrepareExpr
  	 * because it'd result in recursively invoking eval_const_expressions.)
  	 */
! 	exprstate = ExecInitExpr(expr, NULL, false);
  
  	/*
  	 * And evaluate it.
diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c
new file mode 100644
index c3161c5..f98854d
*** a/src/backend/optimizer/util/predtest.c
--- b/src/backend/optimizer/util/predtest.c
*************** btree_predicate_proof(Expr *predicate, N
*** 1486,1492 ****
  	fix_opfuncids((Node *) test_expr);
  
  	/* Prepare it for execution */
! 	test_exprstate = ExecInitExpr(test_expr, NULL);
  
  	/* And execute it. */
  	test_result = ExecEvalExprSwitchContext(test_exprstate,
--- 1486,1492 ----
  	fix_opfuncids((Node *) test_expr);
  
  	/* Prepare it for execution */
! 	test_exprstate = ExecInitExpr(test_expr, NULL, false);
  
  	/* And execute it. */
  	test_result = ExecEvalExprSwitchContext(test_exprstate,
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
new file mode 100644
index 9ad54c5..d7b6ea2
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
*************** isSimpleNode(Node *node, Node *parentNod
*** 4612,4619 ****
  			/* function-like: name(..) or name[..] */
  			return true;
  
! 			/* CASE keywords act as parentheses */
  		case T_CaseExpr:
  			return true;
  
  		case T_FieldSelect:
--- 4612,4623 ----
  			/* function-like: name(..) or name[..] */
  			return true;
  
! 		case T_CacheExpr:
! 			/* hidden from user */
! 			return true;
! 
  		case T_CaseExpr:
+ 			/* CASE keywords act as parentheses */
  			return true;
  
  		case T_FieldSelect:
*************** get_rule_expr(Node *node, deparse_contex
*** 5734,5739 ****
--- 5738,5757 ----
  			}
  			break;
  
+ 		case T_CacheExpr:
+ 			{
+ 				CacheExpr *cache = (CacheExpr *) node;
+ 
+ #ifdef DEBUG_CACHEEXPR
+ 				appendStringInfo(buf, "CACHE[");
+ 				get_rule_expr((Node *) cache->arg, context, true);
+ 				appendStringInfoChar(buf, ']');
+ #else
+ 				get_rule_expr((Node *) cache->arg, context, true);
+ #endif
+ 			}
+ 			break;
+ 
  		case T_CoerceToDomain:
  			{
  				CoerceToDomain *ctest = (CoerceToDomain *) node;
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
new file mode 100644
index 9a74541..56a4bce
*** a/src/include/executor/executor.h
--- b/src/include/executor/executor.h
*************** extern Tuplestorestate *ExecMakeTableFun
*** 227,233 ****
  							bool randomAccess);
  extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext,
  						  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 int	ExecTargetListLength(List *targetlist);
--- 227,233 ----
  							bool randomAccess);
  extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext,
  						  bool *isNull, ExprDoneCond *isDone);
! extern ExprState *ExecInitExpr(Expr *node, PlanState *parent, bool cacheEnabled);
  extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
  extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
  extern int	ExecTargetListLength(List *targetlist);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
new file mode 100644
index 5207102..a4701f2
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
*************** typedef struct FuncExprState
*** 674,679 ****
--- 674,696 ----
  } FuncExprState;
  
  /* ----------------
+  *		CacheExprState node
+  *
+  * Takes care of caching execution-time constants that cannot be constant
+  * folded at plan-time.
+  * ----------------
+  */
+ typedef struct CacheExprState
+ {
+ 	ExprState	xprstate;
+ 	ExprState  *arg;		/* state of sub-expression */
+ 
+ 	bool		enabled;	/* is cache enabled? */
+ 	Datum		result;		/* cached result */
+ 	bool		isNull;		/* is result NULL? */
+ } CacheExprState;
+ 
+ /* ----------------
   *		ScalarArrayOpExprState node
   *
   * This is a FuncExprState plus some additional data.
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
new file mode 100644
index a6dffe6..fa5d8e7
*** a/src/include/nodes/makefuncs.h
--- b/src/include/nodes/makefuncs.h
*************** extern TypeName *makeTypeNameFromOid(Oid
*** 74,79 ****
--- 74,80 ----
  
  extern FuncExpr *makeFuncExpr(Oid funcid, Oid rettype, List *args,
  			 Oid funccollid, Oid inputcollid, CoercionForm fformat);
+ extern CacheExpr *makeCacheExpr(Expr *arg);
  
  extern DefElem *makeDefElem(char *name, Node *arg);
  extern DefElem *makeDefElemExtended(char *nameSpace, char *name, Node *arg,
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
new file mode 100644
index 0e7d184..016a63c
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 172,177 ****
--- 172,178 ----
  	T_JoinExpr,
  	T_FromExpr,
  	T_IntoClause,
+ 	T_CacheExpr,
  
  	/*
  	 * TAGS FOR EXPRESSION STATE NODES (execnodes.h)
*************** typedef enum NodeTag
*** 205,210 ****
--- 206,212 ----
  	T_NullTestState,
  	T_CoerceToDomainState,
  	T_DomainConstraintState,
+ 	T_CacheExprState,
  
  	/*
  	 * TAGS FOR PLANNER NODES (relation.h)
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
new file mode 100644
index 261e7a0..56e1db5
*** a/src/include/nodes/primnodes.h
--- b/src/include/nodes/primnodes.h
*************** typedef struct BooleanTest
*** 1029,1034 ****
--- 1029,1049 ----
  } BooleanTest;
  
  /*
+  * CacheExpr
+  *
+  * CacheExpr is a constant expression to be cached at execution time. The
+  * eval_const_expressions() function inserts CacheExpr nodes nodes at
+  * strategic locations when it recognizes constant expressions that cannot be
+  * constant-folded at plan time, such as expressions with Param references,
+  * stable function and operator calls with constant arguments, etc
+  */
+ typedef struct CacheExpr
+ {
+ 	Expr		xpr;
+ 	Expr	   *arg;
+ } CacheExpr;
+ 
+ /*
   * CoerceToDomain
   *
   * CoerceToDomain represents the operation of coercing a value to a domain
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
new file mode 100644
index bf952b6..faa6408
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
*************** exec_eval_simple_expr(PLpgSQL_execstate
*** 4935,4941 ****
  	if (expr->expr_simple_lxid != curlxid)
  	{
  		oldcontext = MemoryContextSwitchTo(simple_eval_estate->es_query_cxt);
! 		expr->expr_simple_state = ExecInitExpr(expr->expr_simple_expr, NULL);
  		expr->expr_simple_in_use = false;
  		expr->expr_simple_lxid = curlxid;
  		MemoryContextSwitchTo(oldcontext);
--- 4935,4943 ----
  	if (expr->expr_simple_lxid != curlxid)
  	{
  		oldcontext = MemoryContextSwitchTo(simple_eval_estate->es_query_cxt);
! 		expr->expr_simple_state = ExecInitExpr(expr->expr_simple_expr,
! 											   NULL,
! 											   false);
  		expr->expr_simple_in_use = false;
  		expr->expr_simple_lxid = curlxid;
  		MemoryContextSwitchTo(oldcontext);
*************** exec_simple_check_node(Node *node)
*** 5446,5451 ****
--- 5448,5458 ----
  			return TRUE;
  
  		case T_Param:
+ 			/*
+ 			 * If we have other kinds of params here, then earlier tests
+ 			 * should have ruled out this as simple expression
+ 			 */
+ 			Assert(((Param *) node)->paramkind == PARAM_EXTERN);
  			return TRUE;
  
  		case T_ArrayRef:
*************** exec_simple_check_node(Node *node)
*** 5678,5683 ****
--- 5685,5694 ----
  				return TRUE;
  			}
  
+ 		case T_CacheExpr:
+ 			/* Caching is disabled for simple expressions */
+ 			return TRUE;
+ 
  		default:
  			return FALSE;
  	}
diff --git a/src/test/regress/expected/cache.out b/src/test/regress/expected/cache.out
new file mode 100644
index ...592263d
*** a/src/test/regress/expected/cache.out
--- b/src/test/regress/expected/cache.out
***************
*** 0 ****
--- 1,685 ----
+ --
+ -- Test cachable expressions
+ --
+ -- If the NOTICE outputs of these functions change, you've probably broken
+ -- something with the CacheExpr optimization
+ --
+ create function stable_true() returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE TRUE'; return true; end$$;
+ create function volatile_true() returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE TRUE'; return true; end$$;
+ create function stable_false() returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE FALSE'; return false; end$$;
+ create function volatile_false() returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE FALSE'; return false; end$$;
+ -- Table with two rows
+ create table two (i int);
+ insert into two values (1), (2);
+ -- Boolean expressions
+ select stable_false() or volatile_true() or stable_true() as b from two;
+ NOTICE:  STABLE FALSE
+ NOTICE:  STABLE TRUE
+  b 
+ ---
+  t
+  t
+ (2 rows)
+ 
+ select stable_true() or volatile_false() or stable_false() as b from two;
+ NOTICE:  STABLE TRUE
+  b 
+ ---
+  t
+  t
+ (2 rows)
+ 
+ select stable_false() or volatile_true() as b from two;
+ NOTICE:  STABLE FALSE
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE TRUE
+  b 
+ ---
+  t
+  t
+ (2 rows)
+ 
+ select stable_false() or stable_false() or volatile_true() as b from two;
+ NOTICE:  STABLE FALSE
+ NOTICE:  STABLE FALSE
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE TRUE
+  b 
+ ---
+  t
+  t
+ (2 rows)
+ 
+ select volatile_true() or volatile_false() or stable_false() as b from two;
+ NOTICE:  STABLE FALSE
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE TRUE
+  b 
+ ---
+  t
+  t
+ (2 rows)
+ 
+ select volatile_false() or volatile_true() or stable_false() as b from two;
+ NOTICE:  STABLE FALSE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE TRUE
+  b 
+ ---
+  t
+  t
+ (2 rows)
+ 
+ select stable_true() and volatile_false() and stable_false() as b from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  STABLE FALSE
+  b 
+ ---
+  f
+  f
+ (2 rows)
+ 
+ select stable_false() and volatile_true() and stable_true() as b from two;
+ NOTICE:  STABLE FALSE
+  b 
+ ---
+  f
+  f
+ (2 rows)
+ 
+ select stable_true() and volatile_false() as b from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE FALSE
+  b 
+ ---
+  f
+  f
+ (2 rows)
+ 
+ select stable_true() and stable_true() and volatile_false() as b from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  STABLE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE FALSE
+  b 
+ ---
+  f
+  f
+ (2 rows)
+ 
+ select volatile_true() and volatile_false() and stable_true() as b from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE FALSE
+  b 
+ ---
+  f
+  f
+ (2 rows)
+ 
+ select volatile_false() and volatile_true() and stable_true() as b from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE FALSE
+  b 
+ ---
+  f
+  f
+ (2 rows)
+ 
+ select not stable_true() as b from two;
+ NOTICE:  STABLE TRUE
+  b 
+ ---
+  f
+  f
+ (2 rows)
+ 
+ select not volatile_true() as b from two;
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE TRUE
+  b 
+ ---
+  f
+  f
+ (2 rows)
+ 
+ -- Bind params
+ prepare param_test(bool) as select $1 or stable_false() or volatile_true() as b from two;
+ execute param_test(true);
+  b 
+ ---
+  t
+  t
+ (2 rows)
+ 
+ execute param_test(false);
+ NOTICE:  STABLE FALSE
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE TRUE
+  b 
+ ---
+  t
+  t
+ (2 rows)
+ 
+ -- Function calls
+ create function stable(bool) returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE(%)', $1; return $1; end$$;
+ create function volatile(bool) returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE(%)', $1; return $1; end$$;
+ select volatile(volatile_true()) from two;
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE(t)
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE(t)
+  volatile 
+ ----------
+  t
+  t
+ (2 rows)
+ 
+ select stable(stable_true()) from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  STABLE(t)
+  stable 
+ --------
+  t
+  t
+ (2 rows)
+ 
+ select stable(volatile_true()) from two;
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  STABLE(t)
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  STABLE(t)
+  stable 
+ --------
+  t
+  t
+ (2 rows)
+ 
+ select volatile(stable_true()) from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  VOLATILE(t)
+ NOTICE:  VOLATILE(t)
+  volatile 
+ ----------
+  t
+  t
+ (2 rows)
+ 
+ create function stable(bool, bool) returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE(%, %)', $1, $2; return $1; end$$;
+ create function volatile(bool, bool) returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE(%, %)', $1, $2; return $1; end$$;
+ select stable(volatile_true(), volatile_false()) from two;
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  STABLE(t, f)
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  STABLE(t, f)
+  stable 
+ --------
+  t
+  t
+ (2 rows)
+ 
+ select stable(stable_true(), volatile_false()) from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  STABLE(t, f)
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  STABLE(t, f)
+  stable 
+ --------
+  t
+  t
+ (2 rows)
+ 
+ select stable(stable_true(), stable_false()) from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  STABLE FALSE
+ NOTICE:  STABLE(t, f)
+  stable 
+ --------
+  t
+  t
+ (2 rows)
+ 
+ select volatile(volatile_true(), volatile_false()) from two;
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE(t, f)
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE(t, f)
+  volatile 
+ ----------
+  t
+  t
+ (2 rows)
+ 
+ select volatile(stable_true(), volatile_false()) from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE(t, f)
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE(t, f)
+  volatile 
+ ----------
+  t
+  t
+ (2 rows)
+ 
+ select volatile(stable_true(), stable_false()) from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  STABLE FALSE
+ NOTICE:  VOLATILE(t, f)
+ NOTICE:  VOLATILE(t, f)
+  volatile 
+ ----------
+  t
+  t
+ (2 rows)
+ 
+ -- Default arguments
+ create function stable_def(a bool = stable_false(), b bool = volatile_true())
+ returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE(%, %)', $1, $2; return $1; end$$;
+ select stable_def() from two;
+ NOTICE:  STABLE FALSE
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  STABLE(f, t)
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  STABLE(f, t)
+  stable_def 
+ ------------
+  f
+  f
+ (2 rows)
+ 
+ select stable_def(b := stable_true()) from two;
+ NOTICE:  STABLE FALSE
+ NOTICE:  STABLE TRUE
+ NOTICE:  STABLE(f, t)
+  stable_def 
+ ------------
+  f
+  f
+ (2 rows)
+ 
+ select stable_def(volatile_false()) from two;
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  STABLE(f, t)
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  STABLE(f, t)
+  stable_def 
+ ------------
+  f
+  f
+ (2 rows)
+ 
+ -- Operators
+ create function stable_eq(bool, bool) returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE % == %', $1, $2; return $1 = $2; end$$;
+ create function volatile_eq(bool, bool) returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE % =%%= %', $1, $2; return $1 = $2; end$$;
+ create operator == (procedure = stable_eq, leftarg=bool, rightarg=bool);
+ create operator =%= (procedure = volatile_eq, leftarg=bool, rightarg=bool);
+ select volatile_true() == volatile_false() from two;
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  STABLE t == f
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  STABLE t == f
+  ?column? 
+ ----------
+  f
+  f
+ (2 rows)
+ 
+ select stable_true() == volatile_false() from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  STABLE t == f
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  STABLE t == f
+  ?column? 
+ ----------
+  f
+  f
+ (2 rows)
+ 
+ select stable_true() == stable_false() from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  STABLE FALSE
+ NOTICE:  STABLE t == f
+  ?column? 
+ ----------
+  f
+  f
+ (2 rows)
+ 
+ select volatile_true() =%= volatile_false() from two;
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE t =%= f
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE t =%= f
+  ?column? 
+ ----------
+  f
+  f
+ (2 rows)
+ 
+ select stable_true() =%= volatile_false() from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE t =%= f
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE t =%= f
+  ?column? 
+ ----------
+  f
+  f
+ (2 rows)
+ 
+ select stable_true() =%= stable_false() from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  STABLE FALSE
+ NOTICE:  VOLATILE t =%= f
+ NOTICE:  VOLATILE t =%= f
+  ?column? 
+ ----------
+  f
+  f
+ (2 rows)
+ 
+ select (volatile_true() or stable_true()) == true as b from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  STABLE t == t
+ NOTICE:  STABLE t == t
+  b 
+ ---
+  t
+  t
+ (2 rows)
+ 
+ -- Coalesce
+ create function stable_null() returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE NULL'; return null; end$$;
+ create function volatile_null() returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE NULL'; return null; end$$;
+ select coalesce(stable_null(), stable_true()) from two;
+ NOTICE:  STABLE NULL
+ NOTICE:  STABLE TRUE
+  coalesce 
+ ----------
+  t
+  t
+ (2 rows)
+ 
+ select coalesce(stable_true(), volatile_null()) from two;
+ NOTICE:  STABLE TRUE
+  coalesce 
+ ----------
+  t
+  t
+ (2 rows)
+ 
+ select coalesce(volatile_null(), stable_null(), volatile_true()) from two;
+ NOTICE:  VOLATILE NULL
+ NOTICE:  STABLE NULL
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE NULL
+ NOTICE:  VOLATILE TRUE
+  coalesce 
+ ----------
+  t
+  t
+ (2 rows)
+ 
+ -- Case/when
+ select case when stable_true() then 't' else volatile_false() end as b from two;
+ NOTICE:  STABLE TRUE
+  b 
+ ---
+  t
+  t
+ (2 rows)
+ 
+ select case when volatile_true() then stable_true() else stable_false() end as b from two;
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  STABLE TRUE
+ NOTICE:  VOLATILE TRUE
+  b 
+ ---
+  t
+  t
+ (2 rows)
+ 
+ select case when i=1 then stable_true() else stable_false() end as b from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  STABLE FALSE
+  b 
+ ---
+  t
+  f
+ (2 rows)
+ 
+ select case when i=1 then volatile_true() else volatile_false() end as b from two;
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE FALSE
+  b 
+ ---
+  t
+  f
+ (2 rows)
+ 
+ select case when 't' then 't' else volatile_false() end == true as b from two;
+ NOTICE:  STABLE t == t
+  b 
+ ---
+  t
+  t
+ (2 rows)
+ 
+ -- Coerce via I/O
+ select stable_true()::text::bool == true as b from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  STABLE t == t
+  b 
+ ---
+  t
+  t
+ (2 rows)
+ 
+ select volatile_true()::text::bool == true as b from two;
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  STABLE t == t
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  STABLE t == t
+  b 
+ ---
+  t
+  t
+ (2 rows)
+ 
+ -- IS DISTINCT FROM
+ select (stable_true() is not distinct from volatile_false()) as b from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE FALSE
+  b 
+ ---
+  f
+  f
+ (2 rows)
+ 
+ select (stable_true() is distinct from stable_false()) == false as b from two;
+ NOTICE:  STABLE TRUE
+ NOTICE:  STABLE FALSE
+ NOTICE:  STABLE t == f
+  b 
+ ---
+  f
+  f
+ (2 rows)
+ 
+ select (volatile_true() is distinct from null) as b from two;
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE TRUE
+  b 
+ ---
+  t
+  t
+ (2 rows)
+ 
+ -- IS NULL
+ select volatile_true() is null == false as b from two;
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  STABLE f == f
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  STABLE f == f
+  b 
+ ---
+  t
+  t
+ (2 rows)
+ 
+ select stable_null() is not null == true as b from two;
+ NOTICE:  STABLE NULL
+ NOTICE:  STABLE f == t
+  b 
+ ---
+  f
+  f
+ (2 rows)
+ 
+ -- Boolean tests
+ select volatile_false() is true == true as b from two;
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  STABLE f == t
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  STABLE f == t
+  b 
+ ---
+  f
+  f
+ (2 rows)
+ 
+ select stable_null() is not unknown == false as b from two;
+ NOTICE:  STABLE NULL
+ NOTICE:  STABLE f == f
+  b 
+ ---
+  t
+  t
+ (2 rows)
+ 
+ -- Field select -- not currently cached
+ create function stable_row(a out int, b out int) STABLE language plpgsql as
+ $$begin raise notice 'STABLE ROW'; a = 1; b = 2; end$$;
+ select (stable_row()).a from two;
+ NOTICE:  STABLE ROW
+ NOTICE:  STABLE ROW
+  a 
+ ---
+  1
+  1
+ (2 rows)
+ 
+ -- WHERE clause
+ begin;
+ -- stable_true is evaluated twice due to planning estimates
+ declare stable_where cursor for select * from two where i > stable_true()::int;
+ NOTICE:  STABLE TRUE
+ fetch all from stable_where;
+ NOTICE:  STABLE TRUE
+  i 
+ ---
+  2
+ (1 row)
+ 
+ declare volatile_where cursor for select * from two where i = volatile_false()::int;
+ fetch all from volatile_where;
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE FALSE
+  i 
+ ---
+ (0 rows)
+ 
+ rollback;
+ -- INSERT column default expressions
+ create table defaults (
+ 	dummy int,
+ 	a bool default stable_true(),
+ 	b bool default volatile_true()
+ );
+ insert into defaults (dummy) values(0), (1);
+ NOTICE:  STABLE TRUE
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE TRUE
+ -- ALTER COLUMN TYPE USING
+ alter table defaults alter column a type bool using stable_false();
+ NOTICE:  STABLE FALSE
+ alter table defaults alter column a type bool using volatile_false();
+ NOTICE:  VOLATILE FALSE
+ NOTICE:  VOLATILE FALSE
+ -- COPY FROM with default expressions
+ copy defaults (dummy) from stdin;
+ NOTICE:  STABLE TRUE
+ CONTEXT:  COPY defaults, line 1: "2"
+ NOTICE:  VOLATILE TRUE
+ CONTEXT:  COPY defaults, line 1: "2"
+ NOTICE:  VOLATILE TRUE
+ CONTEXT:  COPY defaults, line 2: "3"
+ -- VALUES list expressions
+ -- The fact that there are be 3 lines of 'VOLATILE TRUE' output is a quirk of
+ -- the current set-returning function execution code
+ insert into defaults (dummy, a, b)
+ values (generate_series(4, 5), stable_true(), volatile_true());
+ NOTICE:  STABLE TRUE
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE TRUE
+ NOTICE:  VOLATILE TRUE
+ -- PL/pgSQL Simple expressions
+ -- Make sure we don't cache simple expressions -- these expressions are only
+ -- initialized once per transaction and then executed multiple times
+ create function stable_max() returns int STABLE language plpgsql as
+ $$begin return (select max(i) from two); end$$;
+ create function simple() returns int STABLE language plpgsql as
+ $$begin return stable_max(); end$$;
+ begin;
+ select simple();
+  simple 
+ --------
+       2
+ (1 row)
+ 
+ insert into two values(3);
+ select simple();
+  simple 
+ --------
+       3
+ (1 row)
+ 
+ rollback;
+ -- The end
+ drop table defaults;
+ drop table two;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
new file mode 100644
index d6105f8..13deb89
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
*************** test: select_into select_distinct select
*** 83,89 ****
  # ----------
  # Another group of parallel tests
  # ----------
! test: privileges security_label collate
  
  test: misc
  # rules cannot run concurrently with any test that creates a view
--- 83,89 ----
  # ----------
  # Another group of parallel tests
  # ----------
! test: privileges security_label collate cache
  
  test: misc
  # rules cannot run concurrently with any test that creates a view
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
new file mode 100644
index 90726ce..8ae4ac9
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
*************** test: prepared_xacts
*** 92,97 ****
--- 92,98 ----
  test: privileges
  test: security_label
  test: collate
+ test: cache
  test: misc
  test: rules
  test: select_views
diff --git a/src/test/regress/sql/cache.sql b/src/test/regress/sql/cache.sql
new file mode 100644
index ...9b68670
*** a/src/test/regress/sql/cache.sql
--- b/src/test/regress/sql/cache.sql
***************
*** 0 ****
--- 1,185 ----
+ --
+ -- Test cachable expressions
+ --
+ -- If the NOTICE outputs of these functions change, you've probably broken
+ -- something with the CacheExpr optimization
+ --
+ 
+ create function stable_true() returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE TRUE'; return true; end$$;
+ create function volatile_true() returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE TRUE'; return true; end$$;
+ create function stable_false() returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE FALSE'; return false; end$$;
+ create function volatile_false() returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE FALSE'; return false; end$$;
+ 
+ -- Table with two rows
+ create table two (i int);
+ insert into two values (1), (2);
+ 
+ -- Boolean expressions
+ select stable_false() or volatile_true() or stable_true() as b from two;
+ select stable_true() or volatile_false() or stable_false() as b from two;
+ select stable_false() or volatile_true() as b from two;
+ select stable_false() or stable_false() or volatile_true() as b from two;
+ select volatile_true() or volatile_false() or stable_false() as b from two;
+ select volatile_false() or volatile_true() or stable_false() as b from two;
+ 
+ select stable_true() and volatile_false() and stable_false() as b from two;
+ select stable_false() and volatile_true() and stable_true() as b from two;
+ select stable_true() and volatile_false() as b from two;
+ select stable_true() and stable_true() and volatile_false() as b from two;
+ select volatile_true() and volatile_false() and stable_true() as b from two;
+ select volatile_false() and volatile_true() and stable_true() as b from two;
+ 
+ select not stable_true() as b from two;
+ select not volatile_true() as b from two;
+ 
+ -- Bind params
+ prepare param_test(bool) as select $1 or stable_false() or volatile_true() as b from two;
+ execute param_test(true);
+ execute param_test(false);
+ 
+ -- Function calls
+ create function stable(bool) returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE(%)', $1; return $1; end$$;
+ create function volatile(bool) returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE(%)', $1; return $1; end$$;
+ 
+ select volatile(volatile_true()) from two;
+ select stable(stable_true()) from two;
+ select stable(volatile_true()) from two;
+ select volatile(stable_true()) from two;
+ 
+ create function stable(bool, bool) returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE(%, %)', $1, $2; return $1; end$$;
+ create function volatile(bool, bool) returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE(%, %)', $1, $2; return $1; end$$;
+ 
+ select stable(volatile_true(), volatile_false()) from two;
+ select stable(stable_true(), volatile_false()) from two;
+ select stable(stable_true(), stable_false()) from two;
+ select volatile(volatile_true(), volatile_false()) from two;
+ select volatile(stable_true(), volatile_false()) from two;
+ select volatile(stable_true(), stable_false()) from two;
+ 
+ -- Default arguments
+ create function stable_def(a bool = stable_false(), b bool = volatile_true())
+ returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE(%, %)', $1, $2; return $1; end$$;
+ 
+ select stable_def() from two;
+ select stable_def(b := stable_true()) from two;
+ select stable_def(volatile_false()) from two;
+ 
+ -- Operators
+ create function stable_eq(bool, bool) returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE % == %', $1, $2; return $1 = $2; end$$;
+ create function volatile_eq(bool, bool) returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE % =%%= %', $1, $2; return $1 = $2; end$$;
+ 
+ create operator == (procedure = stable_eq, leftarg=bool, rightarg=bool);
+ create operator =%= (procedure = volatile_eq, leftarg=bool, rightarg=bool);
+ 
+ select volatile_true() == volatile_false() from two;
+ select stable_true() == volatile_false() from two;
+ select stable_true() == stable_false() from two;
+ select volatile_true() =%= volatile_false() from two;
+ select stable_true() =%= volatile_false() from two;
+ select stable_true() =%= stable_false() from two;
+ 
+ select (volatile_true() or stable_true()) == true as b from two;
+ 
+ -- Coalesce
+ create function stable_null() returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE NULL'; return null; end$$;
+ create function volatile_null() returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE NULL'; return null; end$$;
+ 
+ select coalesce(stable_null(), stable_true()) from two;
+ select coalesce(stable_true(), volatile_null()) from two;
+ select coalesce(volatile_null(), stable_null(), volatile_true()) from two;
+ 
+ -- Case/when
+ select case when stable_true() then 't' else volatile_false() end as b from two;
+ select case when volatile_true() then stable_true() else stable_false() end as b from two;
+ select case when i=1 then stable_true() else stable_false() end as b from two;
+ select case when i=1 then volatile_true() else volatile_false() end as b from two;
+ 
+ select case when 't' then 't' else volatile_false() end == true as b from two;
+ 
+ -- Coerce via I/O
+ select stable_true()::text::bool == true as b from two;
+ select volatile_true()::text::bool == true as b from two;
+ 
+ -- IS DISTINCT FROM
+ select (stable_true() is not distinct from volatile_false()) as b from two;
+ select (stable_true() is distinct from stable_false()) == false as b from two;
+ select (volatile_true() is distinct from null) as b from two;
+ 
+ -- IS NULL
+ select volatile_true() is null == false as b from two;
+ select stable_null() is not null == true as b from two;
+ 
+ -- Boolean tests
+ select volatile_false() is true == true as b from two;
+ select stable_null() is not unknown == false as b from two;
+ 
+ -- Field select -- not currently cached
+ create function stable_row(a out int, b out int) STABLE language plpgsql as
+ $$begin raise notice 'STABLE ROW'; a = 1; b = 2; end$$;
+ 
+ select (stable_row()).a from two;
+ 
+ -- WHERE clause
+ begin;
+ -- stable_true is evaluated twice due to planning estimates
+ declare stable_where cursor for select * from two where i > stable_true()::int;
+ fetch all from stable_where;
+ declare volatile_where cursor for select * from two where i = volatile_false()::int;
+ fetch all from volatile_where;
+ rollback;
+ 
+ -- INSERT column default expressions
+ create table defaults (
+ 	dummy int,
+ 	a bool default stable_true(),
+ 	b bool default volatile_true()
+ );
+ insert into defaults (dummy) values(0), (1);
+ 
+ -- ALTER COLUMN TYPE USING
+ alter table defaults alter column a type bool using stable_false();
+ alter table defaults alter column a type bool using volatile_false();
+ 
+ -- COPY FROM with default expressions
+ copy defaults (dummy) from stdin;
+ 2
+ 3
+ \.
+ 
+ -- VALUES list expressions
+ -- The fact that there are be 3 lines of 'VOLATILE TRUE' output is a quirk of
+ -- the current set-returning function execution code
+ insert into defaults (dummy, a, b)
+ values (generate_series(4, 5), stable_true(), volatile_true());
+ 
+ -- PL/pgSQL Simple expressions
+ -- Make sure we don't cache simple expressions -- these expressions are only
+ -- initialized once per transaction and then executed multiple times
+ create function stable_max() returns int STABLE language plpgsql as
+ $$begin return (select max(i) from two); end$$;
+ 
+ create function simple() returns int STABLE language plpgsql as
+ $$begin return stable_max(); end$$;
+ 
+ begin;
+ select simple();
+ insert into two values(3);
+ select simple();
+ rollback;
+ 
+ -- The end
+ drop table defaults;
+ drop table two;
