Tightly packing expressions

Started by Douglas Dooleover 8 years ago2 messages
#1Douglas Doole
dougdoole@gmail.com
1 attachment(s)

Andres,
Back when you were getting ready to commit your faster expressions, I
volunteered to look at switching from an array of fixed sized steps to
tightly packed structures. (
/messages/by-id/20170314221648.jrcgh5n7ld4ej2o7@alap3.anarazel.de).
I've finally found time to make it happen.

Using tightly packed structures makes the expressions a lot smaller. I
instrumented some before and after builds and ran "make check" just to see
how much memory expressions were using. What I found was:

There were ~104K expressions.

Memory - bytes needed to hold the steps of the expressions
Allocated Memory - memory is allocated in blocks, this is the bytes
allocated to hold the expressions
Wasted Memory - the difference between the allocated and used memory

Original code:
Memory: min=64 max=9984 total=28232512 average=265
Allocated Memory: min=1024 max=16384 total=111171584 average=1045
Wasted Memory: min=0 max=6400 total=82939072 average=780

New code:
Memory: min=32 (50%) max=8128 (82%) total=18266712 (65%) average=175 (66%)
Allocated Memory: min=192 (19%) max=9408 (57%) total=29386176 (26%)
average=282 (27%)
Wasted Memory: min=0 (0%) max=1280 (20%) total=11119464 (13%) average=106
(14%)

So on average expressions are about a third smaller than with the fixed
size array. The new approach doesn't overallocate as badly as the array
approach so the packed approach cuts memory consumption by over 70%. (Of
course, the array code could be tuned to reduce the overhead as well.)

Another benefit of the way I did this is that the expression memory is
never reallocated. This means that it is safe for one step to point
directly to a field in another step without needing to separately palloc
storage. That should allow us to simplify the code and reduce pointer
traversals. (I haven't exploited this in the patch. I figured it would be a
task for round 2 assuming you like what I've done here.)

The work was mostly mechanical. The only tricky bit was dealing with the
places where you jump to step n+1 while building step n. Since we can't
tell the address of step n+1 until it is actually built, I had to defer
resolution of the jumps. So all the interesting bits of this patch are in
ExprEvalPushStep().

Let me know what you think.

- Doug
Salesforce

Attachments:

exprBuild.patchtext/x-patch; charset=US-ASCII; name=exprBuild.patchDownload
*** a/src/backend/executor/execExpr.c
--- b/src/backend/executor/execExpr.c
***************
*** 45,50 ****
--- 45,51 ----
  #include "utils/builtins.h"
  #include "utils/lsyscache.h"
  #include "utils/typcache.h"
+ #include "sys/param.h"
  
  
  typedef struct LastAttnumInfo
***************
*** 57,78 **** typedef struct LastAttnumInfo
  static void ExecReadyExpr(ExprState *state);
  static void ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				Datum *resv, bool *resnull);
! static void ExprEvalPushStep(ExprState *es, const ExprEvalStep *s);
! static void ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args,
  			 Oid funcid, Oid inputcollid, PlanState *parent,
! 			 ExprState *state);
  static void ExecInitExprSlots(ExprState *state, Node *node);
  static bool get_last_attnums_walker(Node *node, LastAttnumInfo *info);
! static void ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable,
! 					PlanState *parent);
! static void ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref,
  				 PlanState *parent, ExprState *state,
  				 Datum *resv, bool *resnull);
  static bool isAssignmentIndirectionExpr(Expr *expr);
! static void ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
  					   PlanState *parent, ExprState *state,
  					   Datum *resv, bool *resnull);
  
  
  /*
   * ExecInitExpr: prepare an expression tree for execution
--- 58,166 ----
  static void ExecReadyExpr(ExprState *state);
  static void ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				Datum *resv, bool *resnull);
! static ExprEvalStep *ExprEvalPushStep(ExprState *es, const ExprEvalOp opcode,
!                                       Datum *resv, bool *resnull);
! static void ExecInitFunc(Expr *node, List *args,
  			 Oid funcid, Oid inputcollid, PlanState *parent,
! 			 ExprState *state, Datum *resv, bool *resnull);
  static void ExecInitExprSlots(ExprState *state, Node *node);
  static bool get_last_attnums_walker(Node *node, LastAttnumInfo *info);
! static void ExecInitWholeRowVar(ExprState *state, Var *variable,
!                                 PlanState *parent, Datum *resv, bool *resnull);
! static void ExecInitArrayRef(ArrayRef *aref,
  				 PlanState *parent, ExprState *state,
  				 Datum *resv, bool *resnull);
  static bool isAssignmentIndirectionExpr(Expr *expr);
! static void ExecInitCoerceToDomain(CoerceToDomain *ctest,
  					   PlanState *parent, ExprState *state,
  					   Datum *resv, bool *resnull);
  
+ /*
+  * Lookup array for the size of each op
+  */
+ #define EEOPSIZE(t) MAXALIGN(sizeof(t))
+ static size_t eeop_size[] =
+ {
+ 	/* EEOP_DONE                   */ EEOPSIZE(ExprEvalStep),
+ 	/* EEOP_INNER_FETCHSOME        */ EEOPSIZE(ExprEvalStep_fetch),
+ 	/* EEOP_OUTER_FETCHSOME        */ EEOPSIZE(ExprEvalStep_fetch),
+ 	/* EEOP_SCAN_FETCHSOME         */ EEOPSIZE(ExprEvalStep_fetch),
+ 	/* EEOP_INNER_VAR_FIRST        */ EEOPSIZE(ExprEvalStep_var),
+ 	/* EEOP_INNER_VAR              */ EEOPSIZE(ExprEvalStep_var),
+ 	/* EEOP_OUTER_VAR_FIRST        */ EEOPSIZE(ExprEvalStep_var),
+ 	/* EEOP_OUTER_VAR              */ EEOPSIZE(ExprEvalStep_var),
+ 	/* EEOP_SCAN_VAR_FIRST         */ EEOPSIZE(ExprEvalStep_var),
+ 	/* EEOP_SCAN_VAR               */ EEOPSIZE(ExprEvalStep_var),
+ 	/* EEOP_INNER_SYSVAR           */ EEOPSIZE(ExprEvalStep_var),
+ 	/* EEOP_OUTER_SYSVAR           */ EEOPSIZE(ExprEvalStep_var),
+ 	/* EEOP_SCAN_SYSVAR            */ EEOPSIZE(ExprEvalStep_var),
+ 	/* EEOP_WHOLEROW               */ EEOPSIZE(ExprEvalStep_wholerow),
+ 	/* EEOP_ASSIGN_INNER_VAR       */ EEOPSIZE(ExprEvalStep_assign_var),
+ 	/* EEOP_ASSIGN_OUTER_VAR       */ EEOPSIZE(ExprEvalStep_assign_var),
+ 	/* EEOP_ASSIGN_SCAN_VAR        */ EEOPSIZE(ExprEvalStep_assign_var),
+ 	/* EEOP_ASSIGN_TMP             */ EEOPSIZE(ExprEvalStep_assign_tmp),
+ 	/* EEOP_ASSIGN_TMP_MAKE_RO     */ EEOPSIZE(ExprEvalStep_assign_tmp),
+ 	/* EEOP_CONST                  */ EEOPSIZE(ExprEvalStep_constval),
+ 	/* EEOP_FUNCEXPR               */ EEOPSIZE(ExprEvalStep_func),
+ 	/* EEOP_FUNCEXPR_STRICT        */ EEOPSIZE(ExprEvalStep_func),
+ 	/* EEOP_FUNCEXPR_FUSAGE        */ EEOPSIZE(ExprEvalStep_func),
+ 	/* EEOP_FUNCEXPR_STRICT_FUSAGE */ EEOPSIZE(ExprEvalStep_func),
+ 	/* EEOP_BOOL_AND_STEP_FIRST    */ EEOPSIZE(ExprEvalStep_boolexpr),
+ 	/* EEOP_BOOL_AND_STEP          */ EEOPSIZE(ExprEvalStep_boolexpr),
+ 	/* EEOP_BOOL_AND_STEP_LAST     */ EEOPSIZE(ExprEvalStep_boolexpr),
+ 	/* EEOP_BOOL_OR_STEP_FIRST     */ EEOPSIZE(ExprEvalStep_boolexpr),
+ 	/* EEOP_BOOL_OR_STEP           */ EEOPSIZE(ExprEvalStep_boolexpr),
+ 	/* EEOP_BOOL_OR_STEP_LAST      */ EEOPSIZE(ExprEvalStep_boolexpr),
+ 	/* EEOP_BOOL_NOT_STEP          */ EEOPSIZE(ExprEvalStep_boolexpr),
+ 	/* EEOP_QUAL                   */ EEOPSIZE(ExprEvalStep_qualexpr),
+ 	/* EEOP_JUMP                   */ EEOPSIZE(ExprEvalStep_jump),
+ 	/* EEOP_JUMP_IF_NULL           */ EEOPSIZE(ExprEvalStep_jump),
+ 	/* EEOP_JUMP_IF_NOT_NULL       */ EEOPSIZE(ExprEvalStep_jump),
+ 	/* EEOP_JUMP_IF_NOT_TRUE       */ EEOPSIZE(ExprEvalStep_jump),
+ 	/* EEOP_NULLTEST_ISNULL        */ EEOPSIZE(ExprEvalStep_nulltest_row),
+ 	/* EEOP_NULLTEST_ISNOTNULL     */ EEOPSIZE(ExprEvalStep_nulltest_row),
+ 	/* EEOP_NULLTEST_ROWISNULL     */ EEOPSIZE(ExprEvalStep_nulltest_row),
+ 	/* EEOP_NULLTEST_ROWISNOTNULL  */ EEOPSIZE(ExprEvalStep_nulltest_row),
+ 	/* EEOP_BOOLTEST_IS_TRUE       */ EEOPSIZE(ExprEvalStep),
+ 	/* EEOP_BOOLTEST_IS_NOT_TRUE   */ EEOPSIZE(ExprEvalStep),
+ 	/* EEOP_BOOLTEST_IS_FALSE      */ EEOPSIZE(ExprEvalStep),
+ 	/* EEOP_BOOLTEST_IS_NOT_FALSE  */ EEOPSIZE(ExprEvalStep),
+ 	/* EEOP_PARAM_EXEC             */ EEOPSIZE(ExprEvalStep_param),
+ 	/* EEOP_PARAM_EXTERN           */ EEOPSIZE(ExprEvalStep_param),
+ 	/* EEOP_CASE_TESTVAL           */ EEOPSIZE(ExprEvalStep_casetest),
+ 	/* EEOP_MAKE_READONLY          */ EEOPSIZE(ExprEvalStep_make_readonly),
+ 	/* EEOP_IOCOERCE               */ EEOPSIZE(ExprEvalStep_iocoerce),
+ 	/* EEOP_DISTINCT               */ EEOPSIZE(ExprEvalStep_func),
+ 	/* EEOP_NULLIF                 */ EEOPSIZE(ExprEvalStep_func),
+ 	/* EEOP_SQLVALUEFUNCTION       */ EEOPSIZE(ExprEvalStep_sqlvaluefunction),
+ 	/* EEOP_CURRENTOFEXPR          */ EEOPSIZE(ExprEvalStep),
+ 	/* EEOP_NEXTVALUEEXPR          */ EEOPSIZE(ExprEvalStep_nextvalueexpr),
+ 	/* EEOP_ARRAYEXPR              */ EEOPSIZE(ExprEvalStep_arrayexpr),
+ 	/* EEOP_ARRAYCOERCE            */ EEOPSIZE(ExprEvalStep_arraycoerce),
+ 	/* EEOP_ROW                    */ EEOPSIZE(ExprEvalStep_row),
+ 	/* EEOP_ROWCOMPARE_STEP        */ EEOPSIZE(ExprEvalStep_rowcompare_step),
+ 	/* EEOP_ROWCOMPARE_FINAL       */ EEOPSIZE(ExprEvalStep_rowcompare_final),
+ 	/* EEOP_MINMAX                 */ EEOPSIZE(ExprEvalStep_minmax),
+ 	/* EEOP_FIELDSELECT            */ EEOPSIZE(ExprEvalStep_fieldselect),
+ 	/* EEOP_FIELDSTORE_DEFORM      */ EEOPSIZE(ExprEvalStep_fieldstore),
+ 	/* EEOP_FIELDSTORE_FORM        */ EEOPSIZE(ExprEvalStep_fieldstore),
+ 	/* EEOP_ARRAYREF_SUBSCRIPT     */ EEOPSIZE(ExprEvalStep_arrayref_subscript),
+ 	/* EEOP_ARRAYREF_OLD           */ EEOPSIZE(ExprEvalStep_arrayref),
+ 	/* EEOP_ARRAYREF_ASSIGN        */ EEOPSIZE(ExprEvalStep_arrayref),
+ 	/* EEOP_ARRAYREF_FETCH         */ EEOPSIZE(ExprEvalStep_arrayref),
+ 	/* EEOP_DOMAIN_TESTVAL         */ EEOPSIZE(ExprEvalStep_casetest),
+ 	/* EEOP_DOMAIN_NOTNULL         */ EEOPSIZE(ExprEvalStep_domaincheck),
+ 	/* EEOP_DOMAIN_CHECK           */ EEOPSIZE(ExprEvalStep_domaincheck),
+ 	/* EEOP_CONVERT_ROWTYPE        */ EEOPSIZE(ExprEvalStep_convert_rowtype),
+ 	/* EEOP_SCALARARRAYOP          */ EEOPSIZE(ExprEvalStep_scalararrayop),
+ 	/* EEOP_XMLEXPR                */ EEOPSIZE(ExprEvalStep_xmlexpr),
+ 	/* EEOP_AGGREF                 */ EEOPSIZE(ExprEvalStep_aggref),
+ 	/* EEOP_GROUPING_FUNC          */ EEOPSIZE(ExprEvalStep_grouping_func),
+ 	/* EEOP_WINDOW_FUNC            */ EEOPSIZE(ExprEvalStep_window_func),
+ 	/* EEOP_SUBPLAN                */ EEOPSIZE(ExprEvalStep_subplan),
+ 	/* EEOP_ALTERNATIVE_SUBPLAN    */ EEOPSIZE(ExprEvalStep_alternative_subplan),
+ 	/* EEOP_LAST                   */ 0
+ };
  
  /*
   * ExecInitExpr: prepare an expression tree for execution
***************
*** 113,119 **** ExprState *
  ExecInitExpr(Expr *node, PlanState *parent)
  {
  	ExprState  *state;
- 	ExprEvalStep scratch;
  
  	/* Special case: NULL expression produces a NULL ExprState pointer */
  	if (node == NULL)
--- 201,206 ----
***************
*** 130,137 **** ExecInitExpr(Expr *node, PlanState *parent)
  	ExecInitExprRec(node, parent, state, &state->resvalue, &state->resnull);
  
  	/* Finally, append a DONE step */
! 	scratch.opcode = EEOP_DONE;
! 	ExprEvalPushStep(state, &scratch);
  
  	ExecReadyExpr(state);
  
--- 217,223 ----
  	ExecInitExprRec(node, parent, state, &state->resvalue, &state->resnull);
  
  	/* Finally, append a DONE step */
! 	ExprEvalPushStep(state, EEOP_DONE, NULL, NULL);
  
  	ExecReadyExpr(state);
  
***************
*** 160,166 **** ExprState *
  ExecInitQual(List *qual, PlanState *parent)
  {
  	ExprState  *state;
! 	ExprEvalStep scratch;
  	List	   *adjust_jumps = NIL;
  	ListCell   *lc;
  
--- 246,252 ----
  ExecInitQual(List *qual, PlanState *parent)
  {
  	ExprState  *state;
! 	ExprEvalStep_qualexpr *scratch;
  	List	   *adjust_jumps = NIL;
  	ListCell   *lc;
  
***************
*** 185,197 **** ExecInitQual(List *qual, PlanState *parent)
  	 * special opcode for qual evaluation that's simpler than BOOL_AND (which
  	 * has more complex NULL handling).
  	 */
- 	scratch.opcode = EEOP_QUAL;
- 
- 	/*
- 	 * We can use ExprState's resvalue/resnull as target for each qual expr.
- 	 */
- 	scratch.resvalue = &state->resvalue;
- 	scratch.resnull = &state->resnull;
  
  	foreach(lc, qual)
  	{
--- 271,276 ----
***************
*** 200,229 **** ExecInitQual(List *qual, PlanState *parent)
  		/* first evaluate expression */
  		ExecInitExprRec(node, parent, state, &state->resvalue, &state->resnull);
  
! 		/* then emit EEOP_QUAL to detect if it's false (or null) */
! 		scratch.d.qualexpr.jumpdone = -1;
! 		ExprEvalPushStep(state, &scratch);
! 		adjust_jumps = lappend_int(adjust_jumps,
! 								   state->steps_len - 1);
! 	}
! 
! 	/* adjust jump targets */
! 	foreach(lc, adjust_jumps)
! 	{
! 		ExprEvalStep *as = &state->steps[lfirst_int(lc)];
  
! 		Assert(as->opcode == EEOP_QUAL);
! 		Assert(as->d.qualexpr.jumpdone == -1);
! 		as->d.qualexpr.jumpdone = state->steps_len;
  	}
  
  	/*
  	 * At the end, we don't need to do anything more.  The last qual expr must
  	 * have yielded TRUE, and since its result is stored in the desired output
  	 * location, we're done.
  	 */
! 	scratch.opcode = EEOP_DONE;
! 	ExprEvalPushStep(state, &scratch);
  
  	ExecReadyExpr(state);
  
--- 279,307 ----
  		/* first evaluate expression */
  		ExecInitExprRec(node, parent, state, &state->resvalue, &state->resnull);
  
! 		/*
! 		 * Then emit EEOP_QUAL to detect if it's false (or null)
! 		 * We can use ExprState's resvalue/resnull as target for each qual expr.
! 		 */
! 		scratch = (ExprEvalStep_qualexpr *)
! 			ExprEvalPushStep(state,
! 		                     EEOP_QUAL,
! 		                     &state->resvalue,
! 		                     &state->resnull);
  
! 		scratch->jumpdone = NULL;
! 		adjust_jumps = lappend(adjust_jumps, &scratch->jumpdone);
  	}
  
+ 	/* Resolve the jumpdone pointers when the next operator is created */
+ 	state->adjust_jumps = adjust_jumps;
+ 
  	/*
  	 * At the end, we don't need to do anything more.  The last qual expr must
  	 * have yielded TRUE, and since its result is stored in the desired output
  	 * location, we're done.
  	 */
! 	ExprEvalPushStep(state, EEOP_DONE, NULL, NULL);
  
  	ExecReadyExpr(state);
  
***************
*** 306,312 **** ExecBuildProjectionInfo(List *targetList,
  {
  	ProjectionInfo *projInfo = makeNode(ProjectionInfo);
  	ExprState  *state;
- 	ExprEvalStep scratch;
  	ListCell   *lc;
  
  	projInfo->pi_exprContext = econtext;
--- 384,389 ----
***************
*** 363,395 **** ExecBuildProjectionInfo(List *targetList,
  
  		if (isSafeVar)
  		{
  			/* Fast-path: just generate an EEOP_ASSIGN_*_VAR step */
  			switch (variable->varno)
  			{
  				case INNER_VAR:
  					/* get the tuple from the inner node */
! 					scratch.opcode = EEOP_ASSIGN_INNER_VAR;
  					break;
  
  				case OUTER_VAR:
  					/* get the tuple from the outer node */
! 					scratch.opcode = EEOP_ASSIGN_OUTER_VAR;
  					break;
  
  					/* INDEX_VAR is handled by default case */
  
  				default:
  					/* get the tuple from the relation being scanned */
! 					scratch.opcode = EEOP_ASSIGN_SCAN_VAR;
  					break;
  			}
  
! 			scratch.d.assign_var.attnum = attnum - 1;
! 			scratch.d.assign_var.resultnum = tle->resno - 1;
! 			ExprEvalPushStep(state, &scratch);
  		}
  		else
  		{
  			/*
  			 * Otherwise, compile the column expression normally.
  			 *
--- 440,482 ----
  
  		if (isSafeVar)
  		{
+ 			ExprEvalStep_assign_var *scratch;
+ 			ExprEvalOp opcode;
+ 
  			/* Fast-path: just generate an EEOP_ASSIGN_*_VAR step */
  			switch (variable->varno)
  			{
  				case INNER_VAR:
  					/* get the tuple from the inner node */
! 					opcode = EEOP_ASSIGN_INNER_VAR;
  					break;
  
  				case OUTER_VAR:
  					/* get the tuple from the outer node */
! 					opcode = EEOP_ASSIGN_OUTER_VAR;
  					break;
  
  					/* INDEX_VAR is handled by default case */
  
  				default:
  					/* get the tuple from the relation being scanned */
! 					opcode = EEOP_ASSIGN_SCAN_VAR;
  					break;
  			}
  
! 			scratch = (ExprEvalStep_assign_var *)
! 				ExprEvalPushStep(state,
! 			                     opcode,
! 								 NULL,
! 								 NULL);
! 			scratch->attnum = attnum - 1;
! 			scratch->resultnum = tle->resno - 1;
  		}
  		else
  		{
+ 			ExprEvalStep_assign_tmp *scratch;
+ 			ExprEvalOp opcode;
+ 
  			/*
  			 * Otherwise, compile the column expression normally.
  			 *
***************
*** 406,421 **** ExecBuildProjectionInfo(List *targetList,
  			 * force value to R/O - but only if it could be an expanded datum.
  			 */
  			if (get_typlen(exprType((Node *) tle->expr)) == -1)
! 				scratch.opcode = EEOP_ASSIGN_TMP_MAKE_RO;
  			else
! 				scratch.opcode = EEOP_ASSIGN_TMP;
! 			scratch.d.assign_tmp.resultnum = tle->resno - 1;
! 			ExprEvalPushStep(state, &scratch);
  		}
  	}
  
! 	scratch.opcode = EEOP_DONE;
! 	ExprEvalPushStep(state, &scratch);
  
  	ExecReadyExpr(state);
  
--- 493,511 ----
  			 * force value to R/O - but only if it could be an expanded datum.
  			 */
  			if (get_typlen(exprType((Node *) tle->expr)) == -1)
! 				opcode = EEOP_ASSIGN_TMP_MAKE_RO;
  			else
! 				opcode = EEOP_ASSIGN_TMP;
! 			scratch = (ExprEvalStep_assign_tmp *)
! 				ExprEvalPushStep(state,
! 			                     opcode,
! 								 NULL,
! 								 NULL);
! 			scratch->resultnum = tle->resno - 1;
  		}
  	}
  
! 	ExprEvalPushStep(state, EEOP_DONE, NULL, NULL);
  
  	ExecReadyExpr(state);
  
***************
*** 589,691 **** static void
  ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				Datum *resv, bool *resnull)
  {
! 	ExprEvalStep scratch;
  
  	/* Guard against stack overflow due to overly complex expressions */
  	check_stack_depth();
  
  	/* Step's output location is always what the caller gave us */
  	Assert(resv != NULL && resnull != NULL);
- 	scratch.resvalue = resv;
- 	scratch.resnull = resnull;
  
  	/* cases should be ordered as they are in enum NodeTag */
  	switch (nodeTag(node))
  	{
  		case T_Var:
  			{
  				Var		   *variable = (Var *) node;
  
  				if (variable->varattno == InvalidAttrNumber)
  				{
  					/* whole-row Var */
! 					ExecInitWholeRowVar(&scratch, variable, parent);
  				}
  				else if (variable->varattno <= 0)
  				{
  					/* system column */
- 					scratch.d.var.attnum = variable->varattno;
- 					scratch.d.var.vartype = variable->vartype;
  					switch (variable->varno)
  					{
  						case INNER_VAR:
! 							scratch.opcode = EEOP_INNER_SYSVAR;
  							break;
  						case OUTER_VAR:
! 							scratch.opcode = EEOP_OUTER_SYSVAR;
  							break;
  
  							/* INDEX_VAR is handled by default case */
  
  						default:
! 							scratch.opcode = EEOP_SCAN_SYSVAR;
  							break;
  					}
  				}
  				else
  				{
  					/* regular user column */
- 					scratch.d.var.attnum = variable->varattno - 1;
- 					scratch.d.var.vartype = variable->vartype;
  					/* select EEOP_*_FIRST opcode to force one-time checks */
  					switch (variable->varno)
  					{
  						case INNER_VAR:
! 							scratch.opcode = EEOP_INNER_VAR_FIRST;
  							break;
  						case OUTER_VAR:
! 							scratch.opcode = EEOP_OUTER_VAR_FIRST;
  							break;
  
  							/* INDEX_VAR is handled by default case */
  
  						default:
! 							scratch.opcode = EEOP_SCAN_VAR_FIRST;
  							break;
  					}
  				}
- 
- 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_Const:
  			{
  				Const	   *con = (Const *) node;
  
! 				scratch.opcode = EEOP_CONST;
! 				scratch.d.constval.value = con->constvalue;
! 				scratch.d.constval.isnull = con->constisnull;
! 
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_Param:
  			{
  				Param	   *param = (Param *) node;
  
  				switch (param->paramkind)
  				{
  					case PARAM_EXEC:
! 						scratch.opcode = EEOP_PARAM_EXEC;
! 						scratch.d.param.paramid = param->paramid;
! 						scratch.d.param.paramtype = param->paramtype;
  						break;
  					case PARAM_EXTERN:
! 						scratch.opcode = EEOP_PARAM_EXTERN;
! 						scratch.d.param.paramid = param->paramid;
! 						scratch.d.param.paramtype = param->paramtype;
  						break;
  					default:
  						elog(ERROR, "unrecognized paramkind: %d",
--- 679,789 ----
  ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				Datum *resv, bool *resnull)
  {
! 	ExprEvalOp opcode;
  
  	/* Guard against stack overflow due to overly complex expressions */
  	check_stack_depth();
  
  	/* Step's output location is always what the caller gave us */
  	Assert(resv != NULL && resnull != NULL);
  
  	/* cases should be ordered as they are in enum NodeTag */
  	switch (nodeTag(node))
  	{
  		case T_Var:
  			{
+ 				ExprEvalStep_var *scratch;
  				Var		   *variable = (Var *) node;
  
  				if (variable->varattno == InvalidAttrNumber)
  				{
  					/* whole-row Var */
! 					ExecInitWholeRowVar(state, variable, parent, resv, resnull);
  				}
  				else if (variable->varattno <= 0)
  				{
  					/* system column */
  					switch (variable->varno)
  					{
  						case INNER_VAR:
! 							opcode = EEOP_INNER_SYSVAR;
  							break;
  						case OUTER_VAR:
! 							opcode = EEOP_OUTER_SYSVAR;
  							break;
  
  							/* INDEX_VAR is handled by default case */
  
  						default:
! 							opcode = EEOP_SCAN_SYSVAR;
  							break;
  					}
+ 
+ 					scratch = (ExprEvalStep_var *)
+ 						ExprEvalPushStep(state,
+ 					                     opcode,
+ 										 resv,
+ 										 resnull);
+ 					scratch->attnum = variable->varattno;
+ 					scratch->vartype = variable->vartype;
  				}
  				else
  				{
  					/* regular user column */
  					/* select EEOP_*_FIRST opcode to force one-time checks */
  					switch (variable->varno)
  					{
  						case INNER_VAR:
! 							opcode = EEOP_INNER_VAR_FIRST;
  							break;
  						case OUTER_VAR:
! 							opcode = EEOP_OUTER_VAR_FIRST;
  							break;
  
  							/* INDEX_VAR is handled by default case */
  
  						default:
! 							opcode = EEOP_SCAN_VAR_FIRST;
  							break;
  					}
+ 					scratch = (ExprEvalStep_var *)
+ 						ExprEvalPushStep(state,
+ 					                     opcode,
+ 										 resv,
+ 										 resnull);
+ 					scratch->attnum = variable->varattno - 1;
+ 					scratch->vartype = variable->vartype;
  				}
  				break;
  			}
  
  		case T_Const:
  			{
+ 				ExprEvalStep_constval *scratch;
  				Const	   *con = (Const *) node;
  
! 				scratch = (ExprEvalStep_constval *)
! 					ExprEvalPushStep(state,
! 				                     EEOP_CONST,
! 									 resv,
! 									 resnull);
! 				scratch->value = con->constvalue;
! 				scratch->isnull = con->constisnull;
  				break;
  			}
  
  		case T_Param:
  			{
+ 				ExprEvalStep_param *scratch;
  				Param	   *param = (Param *) node;
  
  				switch (param->paramkind)
  				{
  					case PARAM_EXEC:
! 						opcode = EEOP_PARAM_EXEC;
  						break;
  					case PARAM_EXTERN:
! 						opcode = EEOP_PARAM_EXTERN;
  						break;
  					default:
  						elog(ERROR, "unrecognized paramkind: %d",
***************
*** 693,709 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  						break;
  				}
  
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_Aggref:
  			{
  				Aggref	   *aggref = (Aggref *) node;
  				AggrefExprState *astate = makeNode(AggrefExprState);
  
! 				scratch.opcode = EEOP_AGGREF;
! 				scratch.d.aggref.astate = astate;
  				astate->aggref = aggref;
  
  				if (parent && IsA(parent, AggState))
--- 791,818 ----
  						break;
  				}
  
! 				scratch = (ExprEvalStep_param *)
! 					ExprEvalPushStep(state,
! 				                     opcode,
! 									 resv,
! 									 resnull);
! 				scratch->paramid = param->paramid;
! 				scratch->paramtype = param->paramtype;
  				break;
  			}
  
  		case T_Aggref:
  			{
+ 				ExprEvalStep_aggref *scratch;
  				Aggref	   *aggref = (Aggref *) node;
  				AggrefExprState *astate = makeNode(AggrefExprState);
  
! 				scratch = (ExprEvalStep_aggref *)
! 					ExprEvalPushStep(state,
! 				                     EEOP_AGGREF,
! 									 resv,
! 									 resnull);
! 				scratch->astate = astate;
  				astate->aggref = aggref;
  
  				if (parent && IsA(parent, AggState))
***************
*** 718,730 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  					/* planner messed up */
  					elog(ERROR, "Aggref found in non-Agg plan node");
  				}
- 
- 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_GroupingFunc:
  			{
  				GroupingFunc *grp_node = (GroupingFunc *) node;
  				Agg		   *agg;
  
--- 827,838 ----
  					/* planner messed up */
  					elog(ERROR, "Aggref found in non-Agg plan node");
  				}
  				break;
  			}
  
  		case T_GroupingFunc:
  			{
+ 				ExprEvalStep_grouping_func *scratch;
  				GroupingFunc *grp_node = (GroupingFunc *) node;
  				Agg		   *agg;
  
***************
*** 732,753 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  					!IsA(parent->plan, Agg))
  					elog(ERROR, "GroupingFunc found in non-Agg plan node");
  
! 				scratch.opcode = EEOP_GROUPING_FUNC;
! 				scratch.d.grouping_func.parent = (AggState *) parent;
  
  				agg = (Agg *) (parent->plan);
  
  				if (agg->groupingSets)
! 					scratch.d.grouping_func.clauses = grp_node->cols;
  				else
! 					scratch.d.grouping_func.clauses = NIL;
! 
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_WindowFunc:
  			{
  				WindowFunc *wfunc = (WindowFunc *) node;
  				WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
  
--- 840,864 ----
  					!IsA(parent->plan, Agg))
  					elog(ERROR, "GroupingFunc found in non-Agg plan node");
  
! 				scratch = (ExprEvalStep_grouping_func *)
! 					ExprEvalPushStep(state,
! 				                     EEOP_GROUPING_FUNC,
! 									 resv,
! 									 resnull);
! 				scratch->parent = (AggState *) parent;
  
  				agg = (Agg *) (parent->plan);
  
  				if (agg->groupingSets)
! 					scratch->clauses = grp_node->cols;
  				else
! 					scratch->clauses = NIL;
  				break;
  			}
  
  		case T_WindowFunc:
  			{
+ 				ExprEvalStep_window_func *scratch;
  				WindowFunc *wfunc = (WindowFunc *) node;
  				WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
  
***************
*** 785,793 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  					elog(ERROR, "WindowFunc found in non-WindowAgg plan node");
  				}
  
! 				scratch.opcode = EEOP_WINDOW_FUNC;
! 				scratch.d.window_func.wfstate = wfstate;
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
--- 896,907 ----
  					elog(ERROR, "WindowFunc found in non-WindowAgg plan node");
  				}
  
! 				scratch = (ExprEvalStep_window_func *)
! 					ExprEvalPushStep(state,
! 				                     EEOP_WINDOW_FUNC,
! 									 resv,
! 									 resnull);
! 				scratch->wfstate = wfstate;
  				break;
  			}
  
***************
*** 795,801 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  			{
  				ArrayRef   *aref = (ArrayRef *) node;
  
! 				ExecInitArrayRef(&scratch, aref, parent, state, resv, resnull);
  				break;
  			}
  
--- 909,915 ----
  			{
  				ArrayRef   *aref = (ArrayRef *) node;
  
! 				ExecInitArrayRef(aref, parent, state, resv, resnull);
  				break;
  			}
  
***************
*** 803,812 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  			{
  				FuncExpr   *func = (FuncExpr *) node;
  
! 				ExecInitFunc(&scratch, node,
  							 func->args, func->funcid, func->inputcollid,
! 							 parent, state);
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
--- 917,925 ----
  			{
  				FuncExpr   *func = (FuncExpr *) node;
  
! 				ExecInitFunc(node,
  							 func->args, func->funcid, func->inputcollid,
! 							 parent, state, resv, resnull);
  				break;
  			}
  
***************
*** 814,823 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  			{
  				OpExpr	   *op = (OpExpr *) node;
  
! 				ExecInitFunc(&scratch, node,
  							 op->args, op->opfuncid, op->inputcollid,
! 							 parent, state);
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
--- 927,935 ----
  			{
  				OpExpr	   *op = (OpExpr *) node;
  
! 				ExecInitFunc(node,
  							 op->args, op->opfuncid, op->inputcollid,
! 							 parent, state, resv, resnull);
  				break;
  			}
  
***************
*** 825,833 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  			{
  				DistinctExpr *op = (DistinctExpr *) node;
  
! 				ExecInitFunc(&scratch, node,
  							 op->args, op->opfuncid, op->inputcollid,
! 							 parent, state);
  
  				/*
  				 * Change opcode of call instruction to EEOP_DISTINCT.
--- 937,945 ----
  			{
  				DistinctExpr *op = (DistinctExpr *) node;
  
! 				ExecInitFunc(node,
  							 op->args, op->opfuncid, op->inputcollid,
! 							 parent, state, resv, resnull);
  
  				/*
  				 * Change opcode of call instruction to EEOP_DISTINCT.
***************
*** 838,845 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				 * we decided to do that here, we'd probably want separate
  				 * opcodes for FUSAGE or not.
  				 */
! 				scratch.opcode = EEOP_DISTINCT;
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
--- 950,956 ----
  				 * we decided to do that here, we'd probably want separate
  				 * opcodes for FUSAGE or not.
  				 */
! 				state->last_step->opcode = EEOP_DISTINCT;
  				break;
  			}
  
***************
*** 847,855 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  			{
  				NullIfExpr *op = (NullIfExpr *) node;
  
! 				ExecInitFunc(&scratch, node,
  							 op->args, op->opfuncid, op->inputcollid,
! 							 parent, state);
  
  				/*
  				 * Change opcode of call instruction to EEOP_NULLIF.
--- 958,966 ----
  			{
  				NullIfExpr *op = (NullIfExpr *) node;
  
! 				ExecInitFunc(node,
  							 op->args, op->opfuncid, op->inputcollid,
! 							 parent, state, resv, resnull);
  
  				/*
  				 * Change opcode of call instruction to EEOP_NULLIF.
***************
*** 860,872 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				 * we decided to do that here, we'd probably want separate
  				 * opcodes for FUSAGE or not.
  				 */
! 				scratch.opcode = EEOP_NULLIF;
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_ScalarArrayOpExpr:
  			{
  				ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
  				Expr	   *scalararg;
  				Expr	   *arrayarg;
--- 971,983 ----
  				 * we decided to do that here, we'd probably want separate
  				 * opcodes for FUSAGE or not.
  				 */
! 				state->last_step->opcode = EEOP_NULLIF;
  				break;
  			}
  
  		case T_ScalarArrayOpExpr:
  			{
+ 				ExprEvalStep_scalararrayop *scratch;
  				ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
  				Expr	   *scalararg;
  				Expr	   *arrayarg;
***************
*** 908,934 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				ExecInitExprRec(arrayarg, parent, state, resv, resnull);
  
  				/* And perform the operation */
! 				scratch.opcode = EEOP_SCALARARRAYOP;
! 				scratch.d.scalararrayop.element_type = InvalidOid;
! 				scratch.d.scalararrayop.useOr = opexpr->useOr;
! 				scratch.d.scalararrayop.finfo = finfo;
! 				scratch.d.scalararrayop.fcinfo_data = fcinfo;
! 				scratch.d.scalararrayop.fn_addr = finfo->fn_addr;
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_BoolExpr:
  			{
  				BoolExpr   *boolexpr = (BoolExpr *) node;
  				int			nargs = list_length(boolexpr->args);
  				List	   *adjust_jumps = NIL;
  				int			off;
  				ListCell   *lc;
  
  				/* allocate scratch memory used by all steps of AND/OR */
  				if (boolexpr->boolop != NOT_EXPR)
! 					scratch.d.boolexpr.anynull = (bool *) palloc(sizeof(bool));
  
  				/*
  				 * For each argument evaluate the argument itself, then
--- 1019,1050 ----
  				ExecInitExprRec(arrayarg, parent, state, resv, resnull);
  
  				/* And perform the operation */
! 				scratch = (ExprEvalStep_scalararrayop *)
! 					ExprEvalPushStep(state,
! 				                     EEOP_SCALARARRAYOP,
! 									 resv,
! 									 resnull);
! 				scratch->element_type = InvalidOid;
! 				scratch->useOr = opexpr->useOr;
! 				scratch->finfo = finfo;
! 				scratch->fcinfo_data = fcinfo;
! 				scratch->fn_addr = finfo->fn_addr;
  				break;
  			}
  
  		case T_BoolExpr:
  			{
+ 				ExprEvalStep_boolexpr *scratch;
  				BoolExpr   *boolexpr = (BoolExpr *) node;
  				int			nargs = list_length(boolexpr->args);
  				List	   *adjust_jumps = NIL;
  				int			off;
  				ListCell   *lc;
+ 				bool       *anynull = NULL;
  
  				/* allocate scratch memory used by all steps of AND/OR */
  				if (boolexpr->boolop != NOT_EXPR)
! 					anynull = (bool *) palloc(sizeof(bool));
  
  				/*
  				 * For each argument evaluate the argument itself, then
***************
*** 958,983 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  							Assert(nargs >= 2);
  
  							if (off == 0)
! 								scratch.opcode = EEOP_BOOL_AND_STEP_FIRST;
  							else if (off + 1 == nargs)
! 								scratch.opcode = EEOP_BOOL_AND_STEP_LAST;
  							else
! 								scratch.opcode = EEOP_BOOL_AND_STEP;
  							break;
  						case OR_EXPR:
  							Assert(nargs >= 2);
  
  							if (off == 0)
! 								scratch.opcode = EEOP_BOOL_OR_STEP_FIRST;
  							else if (off + 1 == nargs)
! 								scratch.opcode = EEOP_BOOL_OR_STEP_LAST;
  							else
! 								scratch.opcode = EEOP_BOOL_OR_STEP;
  							break;
  						case NOT_EXPR:
  							Assert(nargs == 1);
  
! 							scratch.opcode = EEOP_BOOL_NOT_STEP;
  							break;
  						default:
  							elog(ERROR, "unrecognized boolop: %d",
--- 1074,1099 ----
  							Assert(nargs >= 2);
  
  							if (off == 0)
! 								opcode = EEOP_BOOL_AND_STEP_FIRST;
  							else if (off + 1 == nargs)
! 								opcode = EEOP_BOOL_AND_STEP_LAST;
  							else
! 								opcode = EEOP_BOOL_AND_STEP;
  							break;
  						case OR_EXPR:
  							Assert(nargs >= 2);
  
  							if (off == 0)
! 								opcode = EEOP_BOOL_OR_STEP_FIRST;
  							else if (off + 1 == nargs)
! 								opcode = EEOP_BOOL_OR_STEP_LAST;
  							else
! 								opcode = EEOP_BOOL_OR_STEP;
  							break;
  						case NOT_EXPR:
  							Assert(nargs == 1);
  
! 							opcode = EEOP_BOOL_NOT_STEP;
  							break;
  						default:
  							elog(ERROR, "unrecognized boolop: %d",
***************
*** 985,1011 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  							break;
  					}
  
! 					scratch.d.boolexpr.jumpdone = -1;
! 					ExprEvalPushStep(state, &scratch);
! 					adjust_jumps = lappend_int(adjust_jumps,
! 											   state->steps_len - 1);
  					off++;
  				}
  
! 				/* adjust jump targets */
! 				foreach(lc, adjust_jumps)
! 				{
! 					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
! 
! 					Assert(as->d.boolexpr.jumpdone == -1);
! 					as->d.boolexpr.jumpdone = state->steps_len;
! 				}
  
  				break;
  			}
  
  		case T_SubPlan:
  			{
  				SubPlan    *subplan = (SubPlan *) node;
  				SubPlanState *sstate;
  
--- 1101,1126 ----
  							break;
  					}
  
! 					scratch = (ExprEvalStep_boolexpr *)
! 						ExprEvalPushStep(state,
! 				                         opcode,
! 										 resv,
! 										 resnull);
! 					scratch->jumpdone = NULL;
! 					scratch->anynull = anynull;
! 					adjust_jumps = lappend(adjust_jumps, &scratch->jumpdone);
  					off++;
  				}
  
! 				/* Resolve the jumpdone pointers when the next operator is created */
! 				state->adjust_jumps = adjust_jumps;
  
  				break;
  			}
  
  		case T_SubPlan:
  			{
+ 				ExprEvalStep_subplan *scratch;
  				SubPlan    *subplan = (SubPlan *) node;
  				SubPlanState *sstate;
  
***************
*** 1017,1031 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				/* add SubPlanState nodes to parent->subPlan */
  				parent->subPlan = lappend(parent->subPlan, sstate);
  
! 				scratch.opcode = EEOP_SUBPLAN;
! 				scratch.d.subplan.sstate = sstate;
! 
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_AlternativeSubPlan:
  			{
  				AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
  				AlternativeSubPlanState *asstate;
  
--- 1132,1149 ----
  				/* add SubPlanState nodes to parent->subPlan */
  				parent->subPlan = lappend(parent->subPlan, sstate);
  
! 				scratch = (ExprEvalStep_subplan *)
! 					ExprEvalPushStep(state,
! 			                         EEOP_SUBPLAN,
! 									 resv,
! 									 resnull);
! 				scratch->sstate = sstate;
  				break;
  			}
  
  		case T_AlternativeSubPlan:
  			{
+ 				ExprEvalStep_alternative_subplan *scratch;
  				AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
  				AlternativeSubPlanState *asstate;
  
***************
*** 1034,1065 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  
  				asstate = ExecInitAlternativeSubPlan(asplan, parent);
  
! 				scratch.opcode = EEOP_ALTERNATIVE_SUBPLAN;
! 				scratch.d.alternative_subplan.asstate = asstate;
! 
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_FieldSelect:
  			{
  				FieldSelect *fselect = (FieldSelect *) node;
  
  				/* evaluate row/record argument into result area */
  				ExecInitExprRec(fselect->arg, parent, state, resv, resnull);
  
  				/* and extract field */
! 				scratch.opcode = EEOP_FIELDSELECT;
! 				scratch.d.fieldselect.fieldnum = fselect->fieldnum;
! 				scratch.d.fieldselect.resulttype = fselect->resulttype;
! 				scratch.d.fieldselect.argdesc = NULL;
! 
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_FieldStore:
  			{
  				FieldStore *fstore = (FieldStore *) node;
  				TupleDesc	tupDesc;
  				TupleDesc  *descp;
--- 1152,1189 ----
  
  				asstate = ExecInitAlternativeSubPlan(asplan, parent);
  
! 				scratch = (ExprEvalStep_alternative_subplan *)
! 					ExprEvalPushStep(state,
! 			                         EEOP_ALTERNATIVE_SUBPLAN,
! 									 resv,
! 									 resnull);
! 				scratch->asstate = asstate;
  				break;
  			}
  
  		case T_FieldSelect:
  			{
+ 				ExprEvalStep_fieldselect *scratch;
  				FieldSelect *fselect = (FieldSelect *) node;
  
  				/* evaluate row/record argument into result area */
  				ExecInitExprRec(fselect->arg, parent, state, resv, resnull);
  
  				/* and extract field */
! 				scratch = (ExprEvalStep_fieldselect *)
! 					ExprEvalPushStep(state,
! 			                         EEOP_FIELDSELECT,
! 									 resv,
! 									 resnull);
! 				scratch->fieldnum = fselect->fieldnum;
! 				scratch->resulttype = fselect->resulttype;
! 				scratch->argdesc = NULL;
  				break;
  			}
  
  		case T_FieldStore:
  			{
+ 				ExprEvalStep_fieldstore *scratch;
  				FieldStore *fstore = (FieldStore *) node;
  				TupleDesc	tupDesc;
  				TupleDesc  *descp;
***************
*** 1086,1098 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				ExecInitExprRec(fstore->arg, parent, state, resv, resnull);
  
  				/* next, deform the input tuple into our workspace */
! 				scratch.opcode = EEOP_FIELDSTORE_DEFORM;
! 				scratch.d.fieldstore.fstore = fstore;
! 				scratch.d.fieldstore.argdesc = descp;
! 				scratch.d.fieldstore.values = values;
! 				scratch.d.fieldstore.nulls = nulls;
! 				scratch.d.fieldstore.ncolumns = ncolumns;
! 				ExprEvalPushStep(state, &scratch);
  
  				/* evaluate new field values, store in workspace columns */
  				forboth(l1, fstore->newvals, l2, fstore->fieldnums)
--- 1210,1225 ----
  				ExecInitExprRec(fstore->arg, parent, state, resv, resnull);
  
  				/* next, deform the input tuple into our workspace */
! 				scratch = (ExprEvalStep_fieldstore *)
! 					ExprEvalPushStep(state,
! 			                         EEOP_FIELDSTORE_DEFORM,
! 									 resv,
! 									 resnull);
! 				scratch->fstore = fstore;
! 				scratch->argdesc = descp;
! 				scratch->values = values;
! 				scratch->nulls = nulls;
! 				scratch->ncolumns = ncolumns;
  
  				/* evaluate new field values, store in workspace columns */
  				forboth(l1, fstore->newvals, l2, fstore->fieldnums)
***************
*** 1143,1155 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				}
  
  				/* finally, form result tuple */
! 				scratch.opcode = EEOP_FIELDSTORE_FORM;
! 				scratch.d.fieldstore.fstore = fstore;
! 				scratch.d.fieldstore.argdesc = descp;
! 				scratch.d.fieldstore.values = values;
! 				scratch.d.fieldstore.nulls = nulls;
! 				scratch.d.fieldstore.ncolumns = ncolumns;
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
--- 1270,1285 ----
  				}
  
  				/* finally, form result tuple */
! 				scratch = (ExprEvalStep_fieldstore *)
! 					ExprEvalPushStep(state,
! 			                         EEOP_FIELDSTORE_FORM,
! 									 resv,
! 									 resnull);
! 				scratch->fstore = fstore;
! 				scratch->argdesc = descp;
! 				scratch->values = values;
! 				scratch->nulls = nulls;
! 				scratch->ncolumns = ncolumns;
  				break;
  			}
  
***************
*** 1164,1169 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
--- 1294,1300 ----
  
  		case T_CoerceViaIO:
  			{
+ 				ExprEvalStep_iocoerce *scratch;
  				CoerceViaIO *iocoerce = (CoerceViaIO *) node;
  				Oid			iofunc;
  				bool		typisvarlena;
***************
*** 1181,1228 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				 * We don't check permissions here as a type's input/output
  				 * function are assumed to be executable by everyone.
  				 */
! 				scratch.opcode = EEOP_IOCOERCE;
  
  				/* lookup the source type's output function */
! 				scratch.d.iocoerce.finfo_out = palloc0(sizeof(FmgrInfo));
! 				scratch.d.iocoerce.fcinfo_data_out = palloc0(sizeof(FunctionCallInfoData));
  
  				getTypeOutputInfo(exprType((Node *) iocoerce->arg),
  								  &iofunc, &typisvarlena);
! 				fmgr_info(iofunc, scratch.d.iocoerce.finfo_out);
! 				fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_out);
! 				InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_out,
! 										 scratch.d.iocoerce.finfo_out,
  										 1, InvalidOid, NULL, NULL);
  
  				/* lookup the result type's input function */
! 				scratch.d.iocoerce.finfo_in = palloc0(sizeof(FmgrInfo));
! 				scratch.d.iocoerce.fcinfo_data_in = palloc0(sizeof(FunctionCallInfoData));
  
  				getTypeInputInfo(iocoerce->resulttype,
  								 &iofunc, &typioparam);
! 				fmgr_info(iofunc, scratch.d.iocoerce.finfo_in);
! 				fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_in);
! 				InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_in,
! 										 scratch.d.iocoerce.finfo_in,
  										 3, InvalidOid, NULL, NULL);
  
  				/*
  				 * We can preload the second and third arguments for the input
  				 * function, since they're constants.
  				 */
! 				fcinfo_in = scratch.d.iocoerce.fcinfo_data_in;
  				fcinfo_in->arg[1] = ObjectIdGetDatum(typioparam);
  				fcinfo_in->argnull[1] = false;
  				fcinfo_in->arg[2] = Int32GetDatum(-1);
  				fcinfo_in->argnull[2] = false;
- 
- 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_ArrayCoerceExpr:
  			{
  				ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
  				Oid			resultelemtype;
  
--- 1312,1362 ----
  				 * We don't check permissions here as a type's input/output
  				 * function are assumed to be executable by everyone.
  				 */
! 				scratch = (ExprEvalStep_iocoerce *)
! 					ExprEvalPushStep(state,
! 			                         EEOP_IOCOERCE,
! 									 resv,
! 									 resnull);
  
  				/* lookup the source type's output function */
! 				scratch->finfo_out = palloc0(sizeof(FmgrInfo));
! 				scratch->fcinfo_data_out = palloc0(sizeof(FunctionCallInfoData));
  
  				getTypeOutputInfo(exprType((Node *) iocoerce->arg),
  								  &iofunc, &typisvarlena);
! 				fmgr_info(iofunc, scratch->finfo_out);
! 				fmgr_info_set_expr((Node *) node, scratch->finfo_out);
! 				InitFunctionCallInfoData(*scratch->fcinfo_data_out,
! 										 scratch->finfo_out,
  										 1, InvalidOid, NULL, NULL);
  
  				/* lookup the result type's input function */
! 				scratch->finfo_in = palloc0(sizeof(FmgrInfo));
! 				scratch->fcinfo_data_in = palloc0(sizeof(FunctionCallInfoData));
  
  				getTypeInputInfo(iocoerce->resulttype,
  								 &iofunc, &typioparam);
! 				fmgr_info(iofunc, scratch->finfo_in);
! 				fmgr_info_set_expr((Node *) node, scratch->finfo_in);
! 				InitFunctionCallInfoData(*scratch->fcinfo_data_in,
! 										 scratch->finfo_in,
  										 3, InvalidOid, NULL, NULL);
  
  				/*
  				 * We can preload the second and third arguments for the input
  				 * function, since they're constants.
  				 */
! 				fcinfo_in = scratch->fcinfo_data_in;
  				fcinfo_in->arg[1] = ObjectIdGetDatum(typioparam);
  				fcinfo_in->argnull[1] = false;
  				fcinfo_in->arg[2] = Int32GetDatum(-1);
  				fcinfo_in->argnull[2] = false;
  				break;
  			}
  
  		case T_ArrayCoerceExpr:
  			{
+ 				ExprEvalStep_arraycoerce *scratch;
  				ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
  				Oid			resultelemtype;
  
***************
*** 1237,1245 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				/* Arrays over domains aren't supported yet */
  				Assert(getBaseType(resultelemtype) == resultelemtype);
  
! 				scratch.opcode = EEOP_ARRAYCOERCE;
! 				scratch.d.arraycoerce.coerceexpr = acoerce;
! 				scratch.d.arraycoerce.resultelemtype = resultelemtype;
  
  				if (OidIsValid(acoerce->elemfuncid))
  				{
--- 1371,1383 ----
  				/* Arrays over domains aren't supported yet */
  				Assert(getBaseType(resultelemtype) == resultelemtype);
  
! 				scratch = (ExprEvalStep_arraycoerce *)
! 					ExprEvalPushStep(state,
! 			                         EEOP_ARRAYCOERCE,
! 									 resv,
! 									 resnull);
! 				scratch->coerceexpr = acoerce;
! 				scratch->resultelemtype = resultelemtype;
  
  				if (OidIsValid(acoerce->elemfuncid))
  				{
***************
*** 1255,1298 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  					InvokeFunctionExecuteHook(acoerce->elemfuncid);
  
  					/* Set up the primary fmgr lookup information */
! 					scratch.d.arraycoerce.elemfunc =
  						(FmgrInfo *) palloc0(sizeof(FmgrInfo));
  					fmgr_info(acoerce->elemfuncid,
! 							  scratch.d.arraycoerce.elemfunc);
  					fmgr_info_set_expr((Node *) acoerce,
! 									   scratch.d.arraycoerce.elemfunc);
  
  					/* Set up workspace for array_map */
! 					scratch.d.arraycoerce.amstate =
  						(ArrayMapState *) palloc0(sizeof(ArrayMapState));
  				}
  				else
  				{
  					/* Don't need workspace if there's no conversion func */
! 					scratch.d.arraycoerce.elemfunc = NULL;
! 					scratch.d.arraycoerce.amstate = NULL;
  				}
- 
- 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_ConvertRowtypeExpr:
  			{
  				ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
  
  				/* evaluate argument into step's result area */
  				ExecInitExprRec(convert->arg, parent, state, resv, resnull);
  
  				/* and push conversion step */
! 				scratch.opcode = EEOP_CONVERT_ROWTYPE;
! 				scratch.d.convert_rowtype.convert = convert;
! 				scratch.d.convert_rowtype.indesc = NULL;
! 				scratch.d.convert_rowtype.outdesc = NULL;
! 				scratch.d.convert_rowtype.map = NULL;
! 				scratch.d.convert_rowtype.initialized = false;
! 
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
--- 1393,1437 ----
  					InvokeFunctionExecuteHook(acoerce->elemfuncid);
  
  					/* Set up the primary fmgr lookup information */
! 					scratch->elemfunc =
  						(FmgrInfo *) palloc0(sizeof(FmgrInfo));
  					fmgr_info(acoerce->elemfuncid,
! 							  scratch->elemfunc);
  					fmgr_info_set_expr((Node *) acoerce,
! 									   scratch->elemfunc);
  
  					/* Set up workspace for array_map */
! 					scratch->amstate =
  						(ArrayMapState *) palloc0(sizeof(ArrayMapState));
  				}
  				else
  				{
  					/* Don't need workspace if there's no conversion func */
! 					scratch->elemfunc = NULL;
! 					scratch->amstate = NULL;
  				}
  				break;
  			}
  
  		case T_ConvertRowtypeExpr:
  			{
+ 				ExprEvalStep_convert_rowtype *scratch;
  				ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
  
  				/* evaluate argument into step's result area */
  				ExecInitExprRec(convert->arg, parent, state, resv, resnull);
  
  				/* and push conversion step */
! 				scratch = (ExprEvalStep_convert_rowtype *)
! 					ExprEvalPushStep(state,
! 			                         EEOP_CONVERT_ROWTYPE,
! 									 resv,
! 									 resnull);
! 				scratch->convert = convert;
! 				scratch->indesc = NULL;
! 				scratch->outdesc = NULL;
! 				scratch->map = NULL;
! 				scratch->initialized = false;
  				break;
  			}
  
***************
*** 1325,1340 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  					 */
  					if (get_typlen(exprType((Node *) caseExpr->arg)) == -1)
  					{
  						/* change caseval in-place */
! 						scratch.opcode = EEOP_MAKE_READONLY;
! 						scratch.resvalue = caseval;
! 						scratch.resnull = casenull;
! 						scratch.d.make_readonly.value = caseval;
! 						scratch.d.make_readonly.isnull = casenull;
! 						ExprEvalPushStep(state, &scratch);
! 						/* restore normal settings of scratch fields */
! 						scratch.resvalue = resv;
! 						scratch.resnull = resnull;
  					}
  				}
  
--- 1464,1479 ----
  					 */
  					if (get_typlen(exprType((Node *) caseExpr->arg)) == -1)
  					{
+ 						ExprEvalStep_make_readonly *scratch;
+ 
  						/* change caseval in-place */
! 						scratch = (ExprEvalStep_make_readonly *)
! 							ExprEvalPushStep(state,
! 					                         EEOP_MAKE_READONLY,
! 											 caseval,
! 											 casenull);
! 						scratch->value = caseval;
! 						scratch->isnull = casenull;
  					}
  				}
  
***************
*** 1346,1355 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				 */
  				foreach(lc, caseExpr->args)
  				{
  					CaseWhen   *when = (CaseWhen *) lfirst(lc);
  					Datum	   *save_innermost_caseval;
  					bool	   *save_innermost_casenull;
! 					int			whenstep;
  
  					/*
  					 * Make testexpr result available to CaseTestExpr nodes
--- 1485,1495 ----
  				 */
  				foreach(lc, caseExpr->args)
  				{
+ 					ExprEvalStep_jump *scratch;
  					CaseWhen   *when = (CaseWhen *) lfirst(lc);
  					Datum	   *save_innermost_caseval;
  					bool	   *save_innermost_casenull;
! 					ExprEvalStep_jump *whenstep;
  
  					/*
  					 * Make testexpr result available to CaseTestExpr nodes
***************
*** 1373,1382 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  					state->innermost_casenull = save_innermost_casenull;
  
  					/* If WHEN result isn't true, jump to next CASE arm */
! 					scratch.opcode = EEOP_JUMP_IF_NOT_TRUE;
! 					scratch.d.jump.jumpdone = -1;	/* computed later */
! 					ExprEvalPushStep(state, &scratch);
! 					whenstep = state->steps_len - 1;
  
  					/*
  					 * If WHEN result is true, evaluate THEN result, storing
--- 1513,1525 ----
  					state->innermost_casenull = save_innermost_casenull;
  
  					/* If WHEN result isn't true, jump to next CASE arm */
! 					scratch = (ExprEvalStep_jump *)
! 						ExprEvalPushStep(state,
! 				                         EEOP_JUMP_IF_NOT_TRUE,
! 										 resv,
! 										 resnull);
! 					scratch->jumpdone = NULL;		/* computed later */
! 					whenstep = scratch;
  
  					/*
  					 * If WHEN result is true, evaluate THEN result, storing
***************
*** 1385,1406 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  					ExecInitExprRec(when->result, parent, state, resv, resnull);
  
  					/* Emit JUMP step to jump to end of CASE's code */
! 					scratch.opcode = EEOP_JUMP;
! 					scratch.d.jump.jumpdone = -1;	/* computed later */
! 					ExprEvalPushStep(state, &scratch);
  
  					/*
  					 * Don't know address for that jump yet, compute once the
  					 * whole CASE expression is built.
  					 */
! 					adjust_jumps = lappend_int(adjust_jumps,
! 											   state->steps_len - 1);
  
  					/*
  					 * But we can set WHEN test's jump target now, to make it
  					 * jump to the next WHEN subexpression or the ELSE.
  					 */
! 					state->steps[whenstep].d.jump.jumpdone = state->steps_len;
  				}
  
  				/* transformCaseExpr always adds a default */
--- 1528,1554 ----
  					ExecInitExprRec(when->result, parent, state, resv, resnull);
  
  					/* Emit JUMP step to jump to end of CASE's code */
! 					scratch = (ExprEvalStep_jump *)
! 						ExprEvalPushStep(state,
! 				                         EEOP_JUMP,
! 										 resv,
! 										 resnull);
! 					scratch->jumpdone = NULL;		/* computed later */
  
  					/*
  					 * Don't know address for that jump yet, compute once the
  					 * whole CASE expression is built.
  					 */
! 					adjust_jumps = lappend(adjust_jumps, &scratch->jumpdone);
  
  					/*
  					 * But we can set WHEN test's jump target now, to make it
  					 * jump to the next WHEN subexpression or the ELSE.
+ 					 * Since we only have one pointer to resolve, just put it
+ 					 * directly into state->adjust_jumps.
  					 */
! 					state->adjust_jumps = lappend(state->adjust_jumps,
! 					                              &whenstep->jumpdone);
  				}
  
  				/* transformCaseExpr always adds a default */
***************
*** 1410,1430 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				ExecInitExprRec(caseExpr->defresult, parent, state,
  								resv, resnull);
  
! 				/* adjust jump targets */
! 				foreach(lc, adjust_jumps)
! 				{
! 					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
! 
! 					Assert(as->opcode == EEOP_JUMP);
! 					Assert(as->d.jump.jumpdone == -1);
! 					as->d.jump.jumpdone = state->steps_len;
! 				}
  
  				break;
  			}
  
  		case T_CaseTestExpr:
  			{
  				/*
  				 * Read from location identified by innermost_caseval.  Note
  				 * that innermost_caseval could be NULL, if this node isn't
--- 1558,1581 ----
  				ExecInitExprRec(caseExpr->defresult, parent, state,
  								resv, resnull);
  
! 				/*
! 				 * Resolve the jumpdone pointers when the next operator is created
! 				 * If it happens that the ELSE expr didn't generate any operators
! 				 * then there will still be an entry in state->adjust_jumps
! 				 * for the last whenstep. We don't want to lose it, so add it
! 				 * to the current list.
! 				 */
! 				if (state->adjust_jumps != NIL)
! 					adjust_jumps = list_concat(adjust_jumps, state->adjust_jumps);
! 				state->adjust_jumps = adjust_jumps;
  
  				break;
  			}
  
  		case T_CaseTestExpr:
  			{
+ 				ExprEvalStep_casetest *scratch;
+ 
  				/*
  				 * Read from location identified by innermost_caseval.  Note
  				 * that innermost_caseval could be NULL, if this node isn't
***************
*** 1433,1474 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				 * supplied in econtext->caseValue_datum.  We'll take care of
  				 * that scenario at runtime.
  				 */
! 				scratch.opcode = EEOP_CASE_TESTVAL;
! 				scratch.d.casetest.value = state->innermost_caseval;
! 				scratch.d.casetest.isnull = state->innermost_casenull;
! 
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_ArrayExpr:
  			{
  				ArrayExpr  *arrayexpr = (ArrayExpr *) node;
  				int			nelems = list_length(arrayexpr->elements);
  				ListCell   *lc;
  				int			elemoff;
  
  				/*
  				 * Evaluate by computing each element, and then forming the
  				 * array.  Elements are computed into scratch arrays
  				 * associated with the ARRAYEXPR step.
  				 */
- 				scratch.opcode = EEOP_ARRAYEXPR;
- 				scratch.d.arrayexpr.elemvalues =
- 					(Datum *) palloc(sizeof(Datum) * nelems);
- 				scratch.d.arrayexpr.elemnulls =
- 					(bool *) palloc(sizeof(bool) * nelems);
- 				scratch.d.arrayexpr.nelems = nelems;
  
! 				/* fill remaining fields of step */
! 				scratch.d.arrayexpr.multidims = arrayexpr->multidims;
! 				scratch.d.arrayexpr.elemtype = arrayexpr->element_typeid;
! 
! 				/* do one-time catalog lookup for type info */
! 				get_typlenbyvalalign(arrayexpr->element_typeid,
! 									 &scratch.d.arrayexpr.elemlength,
! 									 &scratch.d.arrayexpr.elembyval,
! 									 &scratch.d.arrayexpr.elemalign);
  
  				/* prepare to evaluate all arguments */
  				elemoff = 0;
--- 1584,1617 ----
  				 * supplied in econtext->caseValue_datum.  We'll take care of
  				 * that scenario at runtime.
  				 */
! 				scratch = (ExprEvalStep_casetest *)
! 					ExprEvalPushStep(state,
! 			                         EEOP_CASE_TESTVAL,
! 									 resv,
! 									 resnull);
! 				scratch->value = state->innermost_caseval;
! 				scratch->isnull = state->innermost_casenull;
  				break;
  			}
  
  		case T_ArrayExpr:
  			{
+ 				ExprEvalStep_arrayexpr *scratch;
  				ArrayExpr  *arrayexpr = (ArrayExpr *) node;
  				int			nelems = list_length(arrayexpr->elements);
  				ListCell   *lc;
  				int			elemoff;
+ 				Datum      *elemvalues;
+ 				bool       *elemnulls;
  
  				/*
  				 * Evaluate by computing each element, and then forming the
  				 * array.  Elements are computed into scratch arrays
  				 * associated with the ARRAYEXPR step.
  				 */
  
! 				elemvalues = (Datum *) palloc(sizeof(Datum) * nelems);
! 				elemnulls = (bool *) palloc(sizeof(bool) * nelems);
  
  				/* prepare to evaluate all arguments */
  				elemoff = 0;
***************
*** 1477,1497 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  					Expr	   *e = (Expr *) lfirst(lc);
  
  					ExecInitExprRec(e, parent, state,
! 									&scratch.d.arrayexpr.elemvalues[elemoff],
! 									&scratch.d.arrayexpr.elemnulls[elemoff]);
  					elemoff++;
  				}
  
  				/* and then collect all into an array */
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_RowExpr:
  			{
  				RowExpr    *rowexpr = (RowExpr *) node;
  				int			nelems = list_length(rowexpr->args);
  				TupleDesc	tupdesc;
  				Form_pg_attribute *attrs;
  				int			i;
  				ListCell   *l;
--- 1620,1660 ----
  					Expr	   *e = (Expr *) lfirst(lc);
  
  					ExecInitExprRec(e, parent, state,
! 									&elemvalues[elemoff],
! 									&elemnulls[elemoff]);
  					elemoff++;
  				}
  
  				/* and then collect all into an array */
! 				scratch = (ExprEvalStep_arrayexpr *)
! 					ExprEvalPushStep(state,
! 			                         EEOP_ARRAYEXPR,
! 									 resv,
! 									 resnull);
! 				scratch->elemvalues = elemvalues;
! 				scratch->elemnulls = elemnulls;
! 				scratch->nelems = nelems;
! 
! 				/* fill remaining fields of step */
! 				scratch->multidims = arrayexpr->multidims;
! 				scratch->elemtype = arrayexpr->element_typeid;
! 
! 				/* do one-time catalog lookup for type info */
! 				get_typlenbyvalalign(arrayexpr->element_typeid,
! 									 &scratch->elemlength,
! 									 &scratch->elembyval,
! 									 &scratch->elemalign);
  				break;
  			}
  
  		case T_RowExpr:
  			{
+ 				ExprEvalStep_row *scratch;
  				RowExpr    *rowexpr = (RowExpr *) node;
  				int			nelems = list_length(rowexpr->args);
  				TupleDesc	tupdesc;
+ 				Datum      *elemvalues;
+ 				bool       *elemnulls;
  				Form_pg_attribute *attrs;
  				int			i;
  				ListCell   *l;
***************
*** 1527,1542 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				 * Evaluate by first building datums for each field, and then
  				 * a final step forming the composite datum.
  				 */
- 				scratch.opcode = EEOP_ROW;
- 				scratch.d.row.tupdesc = tupdesc;
  
  				/* space for the individual field datums */
! 				scratch.d.row.elemvalues =
! 					(Datum *) palloc(sizeof(Datum) * nelems);
! 				scratch.d.row.elemnulls =
! 					(bool *) palloc(sizeof(bool) * nelems);
  				/* as explained above, make sure any extra columns are null */
! 				memset(scratch.d.row.elemnulls, true, sizeof(bool) * nelems);
  
  				/* Set up evaluation, skipping any deleted columns */
  				attrs = tupdesc->attrs;
--- 1690,1702 ----
  				 * Evaluate by first building datums for each field, and then
  				 * a final step forming the composite datum.
  				 */
  
  				/* space for the individual field datums */
! 				elemvalues = (Datum *) palloc(sizeof(Datum) * nelems);
! 				elemnulls = (bool *) palloc(sizeof(bool) * nelems);
! 
  				/* as explained above, make sure any extra columns are null */
! 				memset(elemnulls, true, sizeof(bool) * nelems);
  
  				/* Set up evaluation, skipping any deleted columns */
  				attrs = tupdesc->attrs;
***************
*** 1572,1589 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  
  					/* Evaluate column expr into appropriate workspace slot */
  					ExecInitExprRec(e, parent, state,
! 									&scratch.d.row.elemvalues[i],
! 									&scratch.d.row.elemnulls[i]);
  					i++;
  				}
  
  				/* And finally build the row value */
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_RowCompareExpr:
  			{
  				RowCompareExpr *rcexpr = (RowCompareExpr *) node;
  				int			nopers = list_length(rcexpr->opnos);
  				List	   *adjust_jumps = NIL;
--- 1732,1757 ----
  
  					/* Evaluate column expr into appropriate workspace slot */
  					ExecInitExprRec(e, parent, state,
! 									&elemvalues[i],
! 									&elemnulls[i]);
  					i++;
  				}
  
  				/* And finally build the row value */
! 				scratch = (ExprEvalStep_row *)
! 					ExprEvalPushStep(state,
! 			                         EEOP_ROW,
! 									 resv,
! 									 resnull);
! 				scratch->tupdesc = tupdesc;
! 				scratch->elemvalues = elemvalues;
! 				scratch->elemnulls = elemnulls;
  				break;
  			}
  
  		case T_RowCompareExpr:
  			{
+ 				ExprEvalStep_rowcompare_final *scratch;
  				RowCompareExpr *rcexpr = (RowCompareExpr *) node;
  				int			nopers = list_length(rcexpr->opnos);
  				List	   *adjust_jumps = NIL;
***************
*** 1620,1625 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
--- 1788,1794 ----
  					 l_opfamily = lnext(l_opfamily),
  					 l_inputcollid = lnext(l_inputcollid))
  				{
+ 					ExprEvalStep_rowcompare_step *scratch;
  					Expr	   *left_expr = (Expr *) lfirst(l_left_expr);
  					Expr	   *right_expr = (Expr *) lfirst(l_right_expr);
  					Oid			opno = lfirst_oid(l_opno);
***************
*** 1665,1681 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  					ExecInitExprRec(right_expr, parent, state,
  									&fcinfo->arg[1], &fcinfo->argnull[1]);
  
! 					scratch.opcode = EEOP_ROWCOMPARE_STEP;
! 					scratch.d.rowcompare_step.finfo = finfo;
! 					scratch.d.rowcompare_step.fcinfo_data = fcinfo;
! 					scratch.d.rowcompare_step.fn_addr = finfo->fn_addr;
  					/* jump targets filled below */
! 					scratch.d.rowcompare_step.jumpnull = -1;
! 					scratch.d.rowcompare_step.jumpdone = -1;
  
! 					ExprEvalPushStep(state, &scratch);
! 					adjust_jumps = lappend_int(adjust_jumps,
! 											   state->steps_len - 1);
  				}
  
  				/*
--- 1834,1852 ----
  					ExecInitExprRec(right_expr, parent, state,
  									&fcinfo->arg[1], &fcinfo->argnull[1]);
  
! 					scratch = (ExprEvalStep_rowcompare_step *)
! 						ExprEvalPushStep(state,
! 				                         EEOP_ROWCOMPARE_STEP,
! 										 resv,
! 										 resnull);
! 					scratch->finfo = finfo;
! 					scratch->fcinfo_data = fcinfo;
! 					scratch->fn_addr = finfo->fn_addr;
  					/* jump targets filled below */
! 					scratch->jumpnull = NULL;
! 					scratch->jumpdone = NULL;
  
! 					adjust_jumps = lappend(adjust_jumps, scratch);
  				}
  
  				/*
***************
*** 1684,1713 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				 */
  				if (nopers == 0)
  				{
! 					scratch.opcode = EEOP_CONST;
! 					scratch.d.constval.value = Int32GetDatum(0);
! 					scratch.d.constval.isnull = false;
! 					ExprEvalPushStep(state, &scratch);
  				}
  
  				/* Finally, examine the last comparison result */
! 				scratch.opcode = EEOP_ROWCOMPARE_FINAL;
! 				scratch.d.rowcompare_final.rctype = rcexpr->rctype;
! 				ExprEvalPushStep(state, &scratch);
  
  				/* adjust jump targetss */
  				foreach(lc, adjust_jumps)
  				{
! 					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
  
  					Assert(as->opcode == EEOP_ROWCOMPARE_STEP);
! 					Assert(as->d.rowcompare_step.jumpdone == -1);
! 					Assert(as->d.rowcompare_step.jumpnull == -1);
  
  					/* jump to comparison evaluation */
! 					as->d.rowcompare_step.jumpdone = state->steps_len - 1;
  					/* jump to the following expression */
! 					as->d.rowcompare_step.jumpnull = state->steps_len;
  				}
  
  				break;
--- 1855,1894 ----
  				 */
  				if (nopers == 0)
  				{
! 					ExprEvalStep_constval *scratch;
! 
! 					scratch = (ExprEvalStep_constval *)
! 						ExprEvalPushStep(state,
! 				                         EEOP_CONST,
! 										 resv,
! 										 resnull);
! 					scratch->value = Int32GetDatum(0);
! 					scratch->isnull = false;
  				}
  
  				/* Finally, examine the last comparison result */
! 				scratch = (ExprEvalStep_rowcompare_final *)
! 					ExprEvalPushStep(state,
! 			                         EEOP_ROWCOMPARE_FINAL,
! 									 resv,
! 									 resnull);
! 				scratch->rctype = rcexpr->rctype;
  
  				/* adjust jump targetss */
  				foreach(lc, adjust_jumps)
  				{
! 					ExprEvalStep_rowcompare_step *as =
! 						(ExprEvalStep_rowcompare_step*)lfirst(lc);
  
  					Assert(as->opcode == EEOP_ROWCOMPARE_STEP);
! 					Assert(as->jumpdone == NULL);
! 					Assert(as->jumpnull == NULL);
  
  					/* jump to comparison evaluation */
! 					as->jumpdone = (ExprEvalStep *)scratch;
  					/* jump to the following expression */
! 					state->adjust_jumps = lappend(state->adjust_jumps,
! 					                              &as->jumpnull);
  				}
  
  				break;
***************
*** 1728,1745 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				 */
  				foreach(lc, coalesce->args)
  				{
  					Expr	   *e = (Expr *) lfirst(lc);
  
  					/* evaluate argument, directly into result datum */
  					ExecInitExprRec(e, parent, state, resv, resnull);
  
  					/* if it's not null, skip to end of COALESCE expr */
! 					scratch.opcode = EEOP_JUMP_IF_NOT_NULL;
! 					scratch.d.jump.jumpdone = -1;	/* adjust later */
! 					ExprEvalPushStep(state, &scratch);
  
! 					adjust_jumps = lappend_int(adjust_jumps,
! 											   state->steps_len - 1);
  				}
  
  				/*
--- 1909,1929 ----
  				 */
  				foreach(lc, coalesce->args)
  				{
+ 					ExprEvalStep_jump *scratch;
  					Expr	   *e = (Expr *) lfirst(lc);
  
  					/* evaluate argument, directly into result datum */
  					ExecInitExprRec(e, parent, state, resv, resnull);
  
  					/* if it's not null, skip to end of COALESCE expr */
! 					scratch = (ExprEvalStep_jump *)
! 						ExprEvalPushStep(state,
! 				                         EEOP_JUMP_IF_NOT_NULL,
! 										 resv,
! 										 resnull);
! 					scratch->jumpdone = NULL;		/* adjust later */
  
! 					adjust_jumps = lappend(adjust_jumps, &scratch->jumpdone);
  				}
  
  				/*
***************
*** 1748,1774 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				 * returned.
  				 */
  
! 				/* adjust jump targets */
! 				foreach(lc, adjust_jumps)
! 				{
! 					ExprEvalStep *as = &state->steps[lfirst_int(lc)];
! 
! 					Assert(as->opcode == EEOP_JUMP_IF_NOT_NULL);
! 					Assert(as->d.jump.jumpdone == -1);
! 					as->d.jump.jumpdone = state->steps_len;
! 				}
  
  				break;
  			}
  
  		case T_MinMaxExpr:
  			{
  				MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
  				int			nelems = list_length(minmaxexpr->args);
  				TypeCacheEntry *typentry;
  				FmgrInfo   *finfo;
  				FunctionCallInfo fcinfo;
  				ListCell   *lc;
  				int			off;
  
  				/* Look up the btree comparison function for the datatype */
--- 1932,1954 ----
  				 * returned.
  				 */
  
! 				/* Resolve the jumpdone pointers when the next operator is created */
! 				state->adjust_jumps = adjust_jumps;
  
  				break;
  			}
  
  		case T_MinMaxExpr:
  			{
+ 				ExprEvalStep_minmax *scratch;
  				MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
  				int			nelems = list_length(minmaxexpr->args);
  				TypeCacheEntry *typentry;
  				FmgrInfo   *finfo;
  				FunctionCallInfo fcinfo;
  				ListCell   *lc;
+ 				Datum      *values;
+ 				bool       *nulls;
  				int			off;
  
  				/* Look up the btree comparison function for the datatype */
***************
*** 1795,1811 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				InitFunctionCallInfoData(*fcinfo, finfo, 2,
  										 minmaxexpr->inputcollid, NULL, NULL);
  
- 				scratch.opcode = EEOP_MINMAX;
  				/* allocate space to store arguments */
! 				scratch.d.minmax.values =
! 					(Datum *) palloc(sizeof(Datum) * nelems);
! 				scratch.d.minmax.nulls =
! 					(bool *) palloc(sizeof(bool) * nelems);
! 				scratch.d.minmax.nelems = nelems;
! 
! 				scratch.d.minmax.op = minmaxexpr->op;
! 				scratch.d.minmax.finfo = finfo;
! 				scratch.d.minmax.fcinfo_data = fcinfo;
  
  				/* evaluate expressions into minmax->values/nulls */
  				off = 0;
--- 1975,1983 ----
  				InitFunctionCallInfoData(*fcinfo, finfo, 2,
  										 minmaxexpr->inputcollid, NULL, NULL);
  
  				/* allocate space to store arguments */
! 				values = (Datum *) palloc(sizeof(Datum) * nelems);
! 				nulls = (bool *) palloc(sizeof(bool) * nelems);
  
  				/* evaluate expressions into minmax->values/nulls */
  				off = 0;
***************
*** 1814,1876 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  					Expr	   *e = (Expr *) lfirst(lc);
  
  					ExecInitExprRec(e, parent, state,
! 									&scratch.d.minmax.values[off],
! 									&scratch.d.minmax.nulls[off]);
  					off++;
  				}
  
  				/* and push the final comparison */
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_SQLValueFunction:
  			{
  				SQLValueFunction *svf = (SQLValueFunction *) node;
  
! 				scratch.opcode = EEOP_SQLVALUEFUNCTION;
! 				scratch.d.sqlvaluefunction.svf = svf;
! 
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_XmlExpr:
  			{
  				XmlExpr    *xexpr = (XmlExpr *) node;
  				int			nnamed = list_length(xexpr->named_args);
  				int			nargs = list_length(xexpr->args);
  				int			off;
  				ListCell   *arg;
! 
! 				scratch.opcode = EEOP_XMLEXPR;
! 				scratch.d.xmlexpr.xexpr = xexpr;
  
  				/* allocate space for storing all the arguments */
  				if (nnamed)
  				{
! 					scratch.d.xmlexpr.named_argvalue =
! 						(Datum *) palloc(sizeof(Datum) * nnamed);
! 					scratch.d.xmlexpr.named_argnull =
! 						(bool *) palloc(sizeof(bool) * nnamed);
! 				}
! 				else
! 				{
! 					scratch.d.xmlexpr.named_argvalue = NULL;
! 					scratch.d.xmlexpr.named_argnull = NULL;
  				}
  
  				if (nargs)
  				{
! 					scratch.d.xmlexpr.argvalue =
! 						(Datum *) palloc(sizeof(Datum) * nargs);
! 					scratch.d.xmlexpr.argnull =
! 						(bool *) palloc(sizeof(bool) * nargs);
! 				}
! 				else
! 				{
! 					scratch.d.xmlexpr.argvalue = NULL;
! 					scratch.d.xmlexpr.argnull = NULL;
  				}
  
  				/* prepare argument execution */
--- 1986,2049 ----
  					Expr	   *e = (Expr *) lfirst(lc);
  
  					ExecInitExprRec(e, parent, state,
! 									&values[off],
! 									&nulls[off]);
  					off++;
  				}
  
  				/* and push the final comparison */
! 				scratch = (ExprEvalStep_minmax *)
! 					ExprEvalPushStep(state,
! 			                         EEOP_MINMAX,
! 									 resv,
! 									 resnull);
! 				scratch->values = values;
! 				scratch->nulls = nulls;
! 				scratch->nelems = nelems;
! 				scratch->op = minmaxexpr->op;
! 				scratch->finfo = finfo;
! 				scratch->fcinfo_data = fcinfo;
  				break;
  			}
  
  		case T_SQLValueFunction:
  			{
+ 				ExprEvalStep_sqlvaluefunction *scratch;
  				SQLValueFunction *svf = (SQLValueFunction *) node;
  
! 				scratch = (ExprEvalStep_sqlvaluefunction *)
! 					ExprEvalPushStep(state,
! 			                         EEOP_SQLVALUEFUNCTION,
! 									 resv,
! 									 resnull);
! 				scratch->svf = svf;
  				break;
  			}
  
  		case T_XmlExpr:
  			{
+ 				ExprEvalStep_xmlexpr *scratch;
  				XmlExpr    *xexpr = (XmlExpr *) node;
  				int			nnamed = list_length(xexpr->named_args);
  				int			nargs = list_length(xexpr->args);
  				int			off;
  				ListCell   *arg;
! 				Datum      *named_argvalue = NULL;
! 				bool       *named_argnull = NULL;
! 				Datum      *argvalue = NULL;
! 				bool       *argnull = NULL;
  
  				/* allocate space for storing all the arguments */
  				if (nnamed)
  				{
! 					named_argvalue = (Datum *) palloc(sizeof(Datum) * nnamed);
! 					named_argnull = (bool *) palloc(sizeof(bool) * nnamed);
  				}
  
  				if (nargs)
  				{
! 					argvalue = (Datum *) palloc(sizeof(Datum) * nargs);
! 					argnull = (bool *) palloc(sizeof(bool) * nargs);
  				}
  
  				/* prepare argument execution */
***************
*** 1880,1887 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  					Expr	   *e = (Expr *) lfirst(arg);
  
  					ExecInitExprRec(e, parent, state,
! 									&scratch.d.xmlexpr.named_argvalue[off],
! 									&scratch.d.xmlexpr.named_argnull[off]);
  					off++;
  				}
  
--- 2053,2060 ----
  					Expr	   *e = (Expr *) lfirst(arg);
  
  					ExecInitExprRec(e, parent, state,
! 									&named_argvalue[off],
! 									&named_argnull[off]);
  					off++;
  				}
  
***************
*** 1891,1938 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  					Expr	   *e = (Expr *) lfirst(arg);
  
  					ExecInitExprRec(e, parent, state,
! 									&scratch.d.xmlexpr.argvalue[off],
! 									&scratch.d.xmlexpr.argnull[off]);
  					off++;
  				}
  
  				/* and evaluate the actual XML expression */
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_NullTest:
  			{
  				NullTest   *ntest = (NullTest *) node;
  
  				if (ntest->nulltesttype == IS_NULL)
  				{
  					if (ntest->argisrow)
! 						scratch.opcode = EEOP_NULLTEST_ROWISNULL;
  					else
! 						scratch.opcode = EEOP_NULLTEST_ISNULL;
  				}
  				else if (ntest->nulltesttype == IS_NOT_NULL)
  				{
  					if (ntest->argisrow)
! 						scratch.opcode = EEOP_NULLTEST_ROWISNOTNULL;
  					else
! 						scratch.opcode = EEOP_NULLTEST_ISNOTNULL;
  				}
  				else
  				{
  					elog(ERROR, "unrecognized nulltesttype: %d",
  						 (int) ntest->nulltesttype);
  				}
- 				/* initialize cache in case it's a row test */
- 				scratch.d.nulltest_row.argdesc = NULL;
  
  				/* first evaluate argument into result variable */
  				ExecInitExprRec(ntest->arg, parent, state,
  								resv, resnull);
  
  				/* then push the test of that argument */
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
--- 2064,2127 ----
  					Expr	   *e = (Expr *) lfirst(arg);
  
  					ExecInitExprRec(e, parent, state,
! 									&argvalue[off],
! 									&argnull[off]);
  					off++;
  				}
  
  				/* and evaluate the actual XML expression */
! 				scratch = (ExprEvalStep_xmlexpr *)
! 					ExprEvalPushStep(state,
! 			                         EEOP_XMLEXPR,
! 									 resv,
! 									 resnull);
! 				scratch->xexpr = xexpr;
! 				scratch->named_argvalue = named_argvalue;
! 				scratch->named_argnull = named_argnull;
! 				scratch->argvalue = argvalue;
! 				scratch->argnull = argnull;
  				break;
  			}
  
  		case T_NullTest:
  			{
+ 				ExprEvalStep_nulltest_row *scratch;
  				NullTest   *ntest = (NullTest *) node;
  
  				if (ntest->nulltesttype == IS_NULL)
  				{
  					if (ntest->argisrow)
! 						opcode = EEOP_NULLTEST_ROWISNULL;
  					else
! 						opcode = EEOP_NULLTEST_ISNULL;
  				}
  				else if (ntest->nulltesttype == IS_NOT_NULL)
  				{
  					if (ntest->argisrow)
! 						opcode = EEOP_NULLTEST_ROWISNOTNULL;
  					else
! 						opcode = EEOP_NULLTEST_ISNOTNULL;
  				}
  				else
  				{
  					elog(ERROR, "unrecognized nulltesttype: %d",
  						 (int) ntest->nulltesttype);
  				}
  
  				/* first evaluate argument into result variable */
  				ExecInitExprRec(ntest->arg, parent, state,
  								resv, resnull);
  
  				/* then push the test of that argument */
! 				scratch = (ExprEvalStep_nulltest_row *)
! 					ExprEvalPushStep(state,
! 			                         opcode,
! 									 resv,
! 									 resnull);
! 
! 				/* initialize cache in case it's a row test */
! 				scratch->argdesc = NULL;
! 
  				break;
  			}
  
***************
*** 1951,1981 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				switch (btest->booltesttype)
  				{
  					case IS_TRUE:
! 						scratch.opcode = EEOP_BOOLTEST_IS_TRUE;
  						break;
  					case IS_NOT_TRUE:
! 						scratch.opcode = EEOP_BOOLTEST_IS_NOT_TRUE;
  						break;
  					case IS_FALSE:
! 						scratch.opcode = EEOP_BOOLTEST_IS_FALSE;
  						break;
  					case IS_NOT_FALSE:
! 						scratch.opcode = EEOP_BOOLTEST_IS_NOT_FALSE;
  						break;
  					case IS_UNKNOWN:
  						/* Same as scalar IS NULL test */
! 						scratch.opcode = EEOP_NULLTEST_ISNULL;
  						break;
  					case IS_NOT_UNKNOWN:
  						/* Same as scalar IS NOT NULL test */
! 						scratch.opcode = EEOP_NULLTEST_ISNOTNULL;
  						break;
  					default:
  						elog(ERROR, "unrecognized booltesttype: %d",
  							 (int) btest->booltesttype);
  				}
  
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
--- 2140,2173 ----
  				switch (btest->booltesttype)
  				{
  					case IS_TRUE:
! 						opcode = EEOP_BOOLTEST_IS_TRUE;
  						break;
  					case IS_NOT_TRUE:
! 						opcode = EEOP_BOOLTEST_IS_NOT_TRUE;
  						break;
  					case IS_FALSE:
! 						opcode = EEOP_BOOLTEST_IS_FALSE;
  						break;
  					case IS_NOT_FALSE:
! 						opcode = EEOP_BOOLTEST_IS_NOT_FALSE;
  						break;
  					case IS_UNKNOWN:
  						/* Same as scalar IS NULL test */
! 						opcode = EEOP_NULLTEST_ISNULL;
  						break;
  					case IS_NOT_UNKNOWN:
  						/* Same as scalar IS NOT NULL test */
! 						opcode = EEOP_NULLTEST_ISNOTNULL;
  						break;
  					default:
  						elog(ERROR, "unrecognized booltesttype: %d",
  							 (int) btest->booltesttype);
  				}
  
! 				ExprEvalPushStep(state,
! 		                         opcode,
! 								 resv,
! 								 resnull);
  				break;
  			}
  
***************
*** 1983,1995 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  			{
  				CoerceToDomain *ctest = (CoerceToDomain *) node;
  
! 				ExecInitCoerceToDomain(&scratch, ctest, parent, state,
  									   resv, resnull);
  				break;
  			}
  
  		case T_CoerceToDomainValue:
  			{
  				/*
  				 * Read from location identified by innermost_domainval.  Note
  				 * that innermost_domainval could be NULL, if we're compiling
--- 2175,2189 ----
  			{
  				CoerceToDomain *ctest = (CoerceToDomain *) node;
  
! 				ExecInitCoerceToDomain(ctest, parent, state,
  									   resv, resnull);
  				break;
  			}
  
  		case T_CoerceToDomainValue:
  			{
+ 				ExprEvalStep_casetest *scratch;
+ 
  				/*
  				 * Read from location identified by innermost_domainval.  Note
  				 * that innermost_domainval could be NULL, if we're compiling
***************
*** 1998,2028 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  				 * econtext->domainValue_datum.  We'll take care of that
  				 * scenario at runtime.
  				 */
! 				scratch.opcode = EEOP_DOMAIN_TESTVAL;
  				/* we share instruction union variant with case testval */
! 				scratch.d.casetest.value = state->innermost_domainval;
! 				scratch.d.casetest.isnull = state->innermost_domainnull;
! 
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_CurrentOfExpr:
  			{
! 				scratch.opcode = EEOP_CURRENTOFEXPR;
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
  		case T_NextValueExpr:
  			{
  				NextValueExpr *nve = (NextValueExpr *) node;
  
! 				scratch.opcode = EEOP_NEXTVALUEEXPR;
! 				scratch.d.nextvalueexpr.seqid = nve->seqid;
! 				scratch.d.nextvalueexpr.seqtypid = nve->typeId;
! 
! 				ExprEvalPushStep(state, &scratch);
  				break;
  			}
  
--- 2192,2230 ----
  				 * econtext->domainValue_datum.  We'll take care of that
  				 * scenario at runtime.
  				 */
! 				scratch = (ExprEvalStep_casetest *)
! 					ExprEvalPushStep(state,
! 			                         EEOP_DOMAIN_TESTVAL,
! 									 resv,
! 									 resnull);
! 
  				/* we share instruction union variant with case testval */
! 				scratch->value = state->innermost_domainval;
! 				scratch->isnull = state->innermost_domainnull;
  				break;
  			}
  
  		case T_CurrentOfExpr:
  			{
! 				ExprEvalPushStep(state,
! 		                         EEOP_CURRENTOFEXPR,
! 								 resv,
! 								 resnull);
  				break;
  			}
  
  		case T_NextValueExpr:
  			{
+ 				ExprEvalStep_nextvalueexpr *scratch;
  				NextValueExpr *nve = (NextValueExpr *) node;
  
! 				scratch = (ExprEvalStep_nextvalueexpr *)
! 					ExprEvalPushStep(state,
! 			                         EEOP_NEXTVALUEEXPR,
! 									 resv,
! 									 resnull);
! 				scratch->seqid = nve->seqid;
! 				scratch->seqtypid = nve->typeId;
  				break;
  			}
  
***************
*** 2035,2074 **** ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
  
  /*
   * Add another expression evaluation step to ExprState->steps.
-  *
-  * Note that this potentially re-allocates es->steps, therefore no pointer
-  * into that array may be used while the expression is still being built.
   */
! static void
! ExprEvalPushStep(ExprState *es, const ExprEvalStep *s)
  {
! 	if (es->steps_alloc == 0)
  	{
! 		es->steps_alloc = 16;
! 		es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
  	}
! 	else if (es->steps_alloc == es->steps_len)
  	{
! 		es->steps_alloc *= 2;
! 		es->steps = repalloc(es->steps,
! 							 sizeof(ExprEvalStep) * es->steps_alloc);
  	}
  
! 	memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
  }
  
  /*
   * Perform setup necessary for the evaluation of a function-like expression,
   * appending argument evaluation steps to the steps list in *state, and
!  * setting up *scratch so it is ready to be pushed.
   *
!  * *scratch is not pushed here, so that callers may override the opcode,
!  * which is useful for function-like cases like DISTINCT.
   */
  static void
! ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
! 			 Oid inputcollid, PlanState *parent, ExprState *state)
  {
  	int			nargs = list_length(args);
  	AclResult	aclresult;
  	FmgrInfo   *flinfo;
--- 2237,2309 ----
  
  /*
   * Add another expression evaluation step to ExprState->steps.
   */
! static ExprEvalStep *
! ExprEvalPushStep(ExprState *es, const ExprEvalOp opcode,
!                  Datum *resv, bool *resnull)
  {
! 	size_t size = eeop_size[opcode];
! 	ExprEvalStep *result = NULL;
! 
! 	if (es->buffer_size < size)
  	{
! 		es->buffer_size = MAX(128, size);
! 		es->step_buffer = palloc0(es->buffer_size);
! 
! 		if (es->steps == NULL)
! 			es->steps = (ExprEvalStep *)es->step_buffer;
  	}
! 
! 	result = (ExprEvalStep *)es->step_buffer;
! 	result->opcode = opcode;
! 	result->resvalue = resv;
! 	result->resnull = resnull;
! 
! 	/* Chain steps */
! 	if (es->last_step != NULL)
! 		es->last_step->nextstep = result;
! 	es->last_step = result;
! 
! 	/* Adjust jump targets if needed */
! 	if (es->adjust_jumps != NIL)
  	{
! 		ListCell   *lc;
! 
! 		foreach(lc, es->adjust_jumps)
! 		{
! 			ExprEvalStep **as = (ExprEvalStep **)lfirst(lc);
! 
! 			Assert(*as == NULL);
! 			*as = result;
! 		}
! 
! 		/* We're done with these pointers */
! 		es->adjust_jumps = NIL;
  	}
  
! 	/* Advance buffer */
! 	es->step_buffer += size;
! 	es->buffer_size -= size;
! 	++es->num_steps;
! 
! 	return result;
  }
  
  /*
   * Perform setup necessary for the evaluation of a function-like expression,
   * appending argument evaluation steps to the steps list in *state, and
!  * pushing the operator.
   *
!  * Callers may override the opcode, which is useful for function-like cases
!  * like DISTINCT, by adjusting state->last_step.
   */
  static void
! ExecInitFunc(Expr *node, List *args, Oid funcid,
! 			 Oid inputcollid, PlanState *parent, ExprState *state,
! 			 Datum *resv, bool *resnull)
  {
+ 	ExprEvalStep_func *scratch;
+ 	ExprEvalOp  opcode;
  	int			nargs = list_length(args);
  	AclResult	aclresult;
  	FmgrInfo   *flinfo;
***************
*** 2097,2106 **** ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
  							   FUNC_MAX_ARGS)));
  
  	/* Allocate function lookup data and parameter workspace for this call */
! 	scratch->d.func.finfo = palloc0(sizeof(FmgrInfo));
! 	scratch->d.func.fcinfo_data = palloc0(sizeof(FunctionCallInfoData));
! 	flinfo = scratch->d.func.finfo;
! 	fcinfo = scratch->d.func.fcinfo_data;
  
  	/* Set up the primary fmgr lookup information */
  	fmgr_info(funcid, flinfo);
--- 2332,2339 ----
  							   FUNC_MAX_ARGS)));
  
  	/* Allocate function lookup data and parameter workspace for this call */
! 	flinfo = palloc0(sizeof(FmgrInfo));
! 	fcinfo = palloc0(sizeof(FunctionCallInfoData));
  
  	/* Set up the primary fmgr lookup information */
  	fmgr_info(funcid, flinfo);
***************
*** 2110,2119 **** ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
  	InitFunctionCallInfoData(*fcinfo, flinfo,
  							 nargs, inputcollid, NULL, NULL);
  
- 	/* Keep extra copies of this info to save an indirection at runtime */
- 	scratch->d.func.fn_addr = flinfo->fn_addr;
- 	scratch->d.func.nargs = nargs;
- 
  	/* We only support non-set functions here */
  	if (flinfo->fn_retset)
  		ereport(ERROR,
--- 2343,2348 ----
***************
*** 2151,2167 **** ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
  	if (pgstat_track_functions <= flinfo->fn_stats)
  	{
  		if (flinfo->fn_strict && nargs > 0)
! 			scratch->opcode = EEOP_FUNCEXPR_STRICT;
  		else
! 			scratch->opcode = EEOP_FUNCEXPR;
  	}
  	else
  	{
  		if (flinfo->fn_strict && nargs > 0)
! 			scratch->opcode = EEOP_FUNCEXPR_STRICT_FUSAGE;
  		else
! 			scratch->opcode = EEOP_FUNCEXPR_FUSAGE;
  	}
  }
  
  /*
--- 2380,2407 ----
  	if (pgstat_track_functions <= flinfo->fn_stats)
  	{
  		if (flinfo->fn_strict && nargs > 0)
! 			opcode = EEOP_FUNCEXPR_STRICT;
  		else
! 			opcode = EEOP_FUNCEXPR;
  	}
  	else
  	{
  		if (flinfo->fn_strict && nargs > 0)
! 			opcode = EEOP_FUNCEXPR_STRICT_FUSAGE;
  		else
! 			opcode = EEOP_FUNCEXPR_FUSAGE;
  	}
+ 	scratch = (ExprEvalStep_func *)
+ 		ExprEvalPushStep(state,
+                          opcode,
+ 						 resv,
+ 						 resnull);
+ 	scratch->finfo = flinfo;
+ 	scratch->fcinfo_data = fcinfo;
+ 
+ 	/* Keep extra copies of function info to save an indirection at runtime */
+ 	scratch->fn_addr = flinfo->fn_addr;
+ 	scratch->nargs = nargs;
  }
  
  /*
***************
*** 2172,2178 **** static void
  ExecInitExprSlots(ExprState *state, Node *node)
  {
  	LastAttnumInfo info = {0, 0, 0};
! 	ExprEvalStep scratch;
  
  	/*
  	 * Figure out which attributes we're going to need.
--- 2412,2418 ----
  ExecInitExprSlots(ExprState *state, Node *node)
  {
  	LastAttnumInfo info = {0, 0, 0};
! 	ExprEvalStep_fetch *scratch;
  
  	/*
  	 * Figure out which attributes we're going to need.
***************
*** 2182,2202 **** ExecInitExprSlots(ExprState *state, Node *node)
  	/* Emit steps as needed */
  	if (info.last_inner > 0)
  	{
! 		scratch.opcode = EEOP_INNER_FETCHSOME;
! 		scratch.d.fetch.last_var = info.last_inner;
! 		ExprEvalPushStep(state, &scratch);
  	}
  	if (info.last_outer > 0)
  	{
! 		scratch.opcode = EEOP_OUTER_FETCHSOME;
! 		scratch.d.fetch.last_var = info.last_outer;
! 		ExprEvalPushStep(state, &scratch);
  	}
  	if (info.last_scan > 0)
  	{
! 		scratch.opcode = EEOP_SCAN_FETCHSOME;
! 		scratch.d.fetch.last_var = info.last_scan;
! 		ExprEvalPushStep(state, &scratch);
  	}
  }
  
--- 2422,2451 ----
  	/* Emit steps as needed */
  	if (info.last_inner > 0)
  	{
! 		scratch = (ExprEvalStep_fetch *)
! 			ExprEvalPushStep(state,
!    	                         EEOP_INNER_FETCHSOME,
! 							 NULL,
! 							 NULL);
! 		scratch->last_var = info.last_inner;
  	}
  	if (info.last_outer > 0)
  	{
! 		scratch = (ExprEvalStep_fetch *)
! 			ExprEvalPushStep(state,
!    	                         EEOP_OUTER_FETCHSOME,
! 							 NULL,
! 							 NULL);
! 		scratch->last_var = info.last_outer;
  	}
  	if (info.last_scan > 0)
  	{
! 		scratch = (ExprEvalStep_fetch *)
! 			ExprEvalPushStep(state,
!    	                         EEOP_SCAN_FETCHSOME,
! 							 NULL,
! 							 NULL);
! 		scratch->last_var = info.last_scan;
  	}
  }
  
***************
*** 2249,2267 **** get_last_attnums_walker(Node *node, LastAttnumInfo *info)
  }
  
  /*
!  * Prepare step for the evaluation of a whole-row variable.
!  * The caller still has to push the step.
   */
  static void
! ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, PlanState *parent)
  {
  	/* fill in all but the target */
! 	scratch->opcode = EEOP_WHOLEROW;
! 	scratch->d.wholerow.var = variable;
! 	scratch->d.wholerow.first = true;
! 	scratch->d.wholerow.slow = false;
! 	scratch->d.wholerow.tupdesc = NULL; /* filled at runtime */
! 	scratch->d.wholerow.junkFilter = NULL;
  
  	/*
  	 * If the input tuple came from a subquery, it might contain "resjunk"
--- 2498,2522 ----
  }
  
  /*
!  * Prepare and push step for the evaluation of a whole-row variable.
   */
  static void
! ExecInitWholeRowVar(ExprState *state, Var *variable,
!                     PlanState *parent, Datum *resv, bool *resnull)
  {
+ 	ExprEvalStep_wholerow *scratch;
+ 
  	/* fill in all but the target */
! 	scratch = (ExprEvalStep_wholerow *)
! 		ExprEvalPushStep(state,
!                          EEOP_WHOLEROW,
! 						 resv,
! 						 resnull);
! 	scratch->var = variable;
! 	scratch->first = true;
! 	scratch->slow = false;
! 	scratch->tupdesc = NULL; /* filled at runtime */
! 	scratch->junkFilter = NULL;
  
  	/*
  	 * If the input tuple came from a subquery, it might contain "resjunk"
***************
*** 2311,2317 **** ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, PlanState *parent)
  			/* If so, build the junkfilter now */
  			if (junk_filter_needed)
  			{
! 				scratch->d.wholerow.junkFilter =
  					ExecInitJunkFilter(subplan->plan->targetlist,
  									   ExecGetResultType(subplan)->tdhasoid,
  									   ExecInitExtraTupleSlot(parent->state));
--- 2566,2572 ----
  			/* If so, build the junkfilter now */
  			if (junk_filter_needed)
  			{
! 				scratch->junkFilter =
  					ExecInitJunkFilter(subplan->plan->targetlist,
  									   ExecGetResultType(subplan)->tdhasoid,
  									   ExecInitExtraTupleSlot(parent->state));
***************
*** 2324,2330 **** ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, PlanState *parent)
   * Prepare evaluation of an ArrayRef expression.
   */
  static void
! ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent,
  				 ExprState *state, Datum *resv, bool *resnull)
  {
  	bool		isAssignment = (aref->refassgnexpr != NULL);
--- 2579,2585 ----
   * Prepare evaluation of an ArrayRef expression.
   */
  static void
! ExecInitArrayRef(ArrayRef *aref, PlanState *parent,
  				 ExprState *state, Datum *resv, bool *resnull)
  {
  	bool		isAssignment = (aref->refassgnexpr != NULL);
***************
*** 2357,2367 **** ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent,
  	 */
  	if (!isAssignment)
  	{
! 		scratch->opcode = EEOP_JUMP_IF_NULL;
! 		scratch->d.jump.jumpdone = -1;	/* adjust later */
! 		ExprEvalPushStep(state, scratch);
! 		adjust_jumps = lappend_int(adjust_jumps,
! 								   state->steps_len - 1);
  	}
  
  	/* Verify subscript list lengths are within limit */
--- 2612,2626 ----
  	 */
  	if (!isAssignment)
  	{
! 		ExprEvalStep_jump *scratch;
! 
! 		scratch = (ExprEvalStep_jump *)
! 			ExprEvalPushStep(state,
!        	                     EEOP_JUMP_IF_NULL,
! 							 resv,
! 							 resnull);
! 		scratch->jumpdone = NULL;	/* adjust later */
! 		adjust_jumps = lappend(adjust_jumps, &scratch->jumpdone);
  	}
  
  	/* Verify subscript list lengths are within limit */
***************
*** 2381,2386 **** ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent,
--- 2640,2646 ----
  	i = 0;
  	foreach(lc, aref->refupperindexpr)
  	{
+ 		ExprEvalStep_arrayref_subscript *scratch;
  		Expr	   *e = (Expr *) lfirst(lc);
  
  		/* When slicing, individual subscript bounds can be omitted */
***************
*** 2398,2411 **** ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent,
  						&arefstate->subscriptvalue, &arefstate->subscriptnull);
  
  		/* ... and then ARRAYREF_SUBSCRIPT saves it into step's workspace */
! 		scratch->opcode = EEOP_ARRAYREF_SUBSCRIPT;
! 		scratch->d.arrayref_subscript.state = arefstate;
! 		scratch->d.arrayref_subscript.off = i;
! 		scratch->d.arrayref_subscript.isupper = true;
! 		scratch->d.arrayref_subscript.jumpdone = -1;	/* adjust later */
! 		ExprEvalPushStep(state, scratch);
! 		adjust_jumps = lappend_int(adjust_jumps,
! 								   state->steps_len - 1);
  		i++;
  	}
  	arefstate->numupper = i;
--- 2658,2673 ----
  						&arefstate->subscriptvalue, &arefstate->subscriptnull);
  
  		/* ... and then ARRAYREF_SUBSCRIPT saves it into step's workspace */
! 		scratch = (ExprEvalStep_arrayref_subscript *)
! 			ExprEvalPushStep(state,
!        	                     EEOP_ARRAYREF_SUBSCRIPT,
! 							 resv,
! 							 resnull);
! 		scratch->state = arefstate;
! 		scratch->off = i;
! 		scratch->isupper = true;
! 		scratch->jumpdone = NULL;	/* adjust later */
! 		adjust_jumps = lappend(adjust_jumps, &scratch->jumpdone);
  		i++;
  	}
  	arefstate->numupper = i;
***************
*** 2414,2419 **** ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent,
--- 2676,2682 ----
  	i = 0;
  	foreach(lc, aref->reflowerindexpr)
  	{
+ 		ExprEvalStep_arrayref_subscript *scratch;
  		Expr	   *e = (Expr *) lfirst(lc);
  
  		/* When slicing, individual subscript bounds can be omitted */
***************
*** 2431,2444 **** ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent,
  						&arefstate->subscriptvalue, &arefstate->subscriptnull);
  
  		/* ... and then ARRAYREF_SUBSCRIPT saves it into step's workspace */
! 		scratch->opcode = EEOP_ARRAYREF_SUBSCRIPT;
! 		scratch->d.arrayref_subscript.state = arefstate;
! 		scratch->d.arrayref_subscript.off = i;
! 		scratch->d.arrayref_subscript.isupper = false;
! 		scratch->d.arrayref_subscript.jumpdone = -1;	/* adjust later */
! 		ExprEvalPushStep(state, scratch);
! 		adjust_jumps = lappend_int(adjust_jumps,
! 								   state->steps_len - 1);
  		i++;
  	}
  	arefstate->numlower = i;
--- 2694,2709 ----
  						&arefstate->subscriptvalue, &arefstate->subscriptnull);
  
  		/* ... and then ARRAYREF_SUBSCRIPT saves it into step's workspace */
! 		scratch = (ExprEvalStep_arrayref_subscript *)
! 			ExprEvalPushStep(state,
!        	                     EEOP_ARRAYREF_SUBSCRIPT,
! 							 resv,
! 							 resnull);
! 		scratch->state = arefstate;
! 		scratch->off = i;
! 		scratch->isupper = false;
! 		scratch->jumpdone = NULL;	/* adjust later */
! 		adjust_jumps = lappend(adjust_jumps, &scratch->jumpdone);
  		i++;
  	}
  	arefstate->numlower = i;
***************
*** 2450,2455 **** ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent,
--- 2715,2721 ----
  
  	if (isAssignment)
  	{
+ 		ExprEvalStep_arrayref *scratch;
  		Datum	   *save_innermost_caseval;
  		bool	   *save_innermost_casenull;
  
***************
*** 2469,2477 **** ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent,
  		 */
  		if (isAssignmentIndirectionExpr(aref->refassgnexpr))
  		{
! 			scratch->opcode = EEOP_ARRAYREF_OLD;
! 			scratch->d.arrayref.state = arefstate;
! 			ExprEvalPushStep(state, scratch);
  		}
  
  		/* ARRAYREF_OLD puts extracted value into prevvalue/prevnull */
--- 2735,2746 ----
  		 */
  		if (isAssignmentIndirectionExpr(aref->refassgnexpr))
  		{
! 			scratch = (ExprEvalStep_arrayref *)
! 				ExprEvalPushStep(state,
!    	    	                     EEOP_ARRAYREF_OLD,
! 								 resv,
! 								 resnull);
! 			scratch->state = arefstate;
  		}
  
  		/* ARRAYREF_OLD puts extracted value into prevvalue/prevnull */
***************
*** 2488,2522 **** ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent,
  		state->innermost_casenull = save_innermost_casenull;
  
  		/* and perform the assignment */
! 		scratch->opcode = EEOP_ARRAYREF_ASSIGN;
! 		scratch->d.arrayref.state = arefstate;
! 		ExprEvalPushStep(state, scratch);
  	}
  	else
  	{
  		/* array fetch is much simpler */
! 		scratch->opcode = EEOP_ARRAYREF_FETCH;
! 		scratch->d.arrayref.state = arefstate;
! 		ExprEvalPushStep(state, scratch);
  	}
  
! 	/* adjust jump targets */
! 	foreach(lc, adjust_jumps)
! 	{
! 		ExprEvalStep *as = &state->steps[lfirst_int(lc)];
! 
! 		if (as->opcode == EEOP_ARRAYREF_SUBSCRIPT)
! 		{
! 			Assert(as->d.arrayref_subscript.jumpdone == -1);
! 			as->d.arrayref_subscript.jumpdone = state->steps_len;
! 		}
! 		else
! 		{
! 			Assert(as->opcode == EEOP_JUMP_IF_NULL);
! 			Assert(as->d.jump.jumpdone == -1);
! 			as->d.jump.jumpdone = state->steps_len;
! 		}
! 	}
  }
  
  /*
--- 2757,2784 ----
  		state->innermost_casenull = save_innermost_casenull;
  
  		/* and perform the assignment */
! 		scratch = (ExprEvalStep_arrayref *)
! 			ExprEvalPushStep(state,
!        	                     EEOP_ARRAYREF_ASSIGN,
! 							 resv,
! 							 resnull);
! 		scratch->state = arefstate;
  	}
  	else
  	{
+ 		ExprEvalStep_arrayref *scratch;
+ 
  		/* array fetch is much simpler */
! 		scratch = (ExprEvalStep_arrayref *)
! 			ExprEvalPushStep(state,
!        	                     EEOP_ARRAYREF_FETCH,
! 							 resv,
! 							 resnull);
! 		scratch->state = arefstate;
  	}
  
! 	/* Resolve the jumpdone pointers when the next operator is created */
! 	state->adjust_jumps = adjust_jumps;
  }
  
  /*
***************
*** 2558,2568 **** isAssignmentIndirectionExpr(Expr *expr)
   * Prepare evaluation of a CoerceToDomain expression.
   */
  static void
! ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
  					   PlanState *parent, ExprState *state,
  					   Datum *resv, bool *resnull)
  {
! 	ExprEvalStep scratch2;
  	DomainConstraintRef *constraint_ref;
  	Datum	   *domainval = NULL;
  	bool	   *domainnull = NULL;
--- 2820,2830 ----
   * Prepare evaluation of a CoerceToDomain expression.
   */
  static void
! ExecInitCoerceToDomain(CoerceToDomain *ctest,
  					   PlanState *parent, ExprState *state,
  					   Datum *resv, bool *resnull)
  {
! 	ExprEvalStep_domaincheck *scratch;
  	DomainConstraintRef *constraint_ref;
  	Datum	   *domainval = NULL;
  	bool	   *domainnull = NULL;
***************
*** 2570,2579 **** ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
  	bool	   *save_innermost_domainnull;
  	ListCell   *l;
  
- 	scratch->d.domaincheck.resulttype = ctest->resulttype;
  	/* we'll allocate workspace only if needed */
! 	scratch->d.domaincheck.checkvalue = NULL;
! 	scratch->d.domaincheck.checknull = NULL;
  
  	/*
  	 * Evaluate argument - it's fine to directly store it into resv/resnull,
--- 2832,2840 ----
  	bool	   *save_innermost_domainnull;
  	ListCell   *l;
  
  	/* we'll allocate workspace only if needed */
! 	Datum      *checkvalue = NULL;
! 	bool       *checknull = NULL;
  
  	/*
  	 * Evaluate argument - it's fine to directly store it into resv/resnull,
***************
*** 2615,2636 **** ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
  	{
  		DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
  
- 		scratch->d.domaincheck.constraintname = con->name;
- 
  		switch (con->constrainttype)
  		{
  			case DOM_CONSTRAINT_NOTNULL:
! 				scratch->opcode = EEOP_DOMAIN_NOTNULL;
! 				ExprEvalPushStep(state, scratch);
  				break;
  			case DOM_CONSTRAINT_CHECK:
  				/* Allocate workspace for CHECK output if we didn't yet */
! 				if (scratch->d.domaincheck.checkvalue == NULL)
  				{
! 					scratch->d.domaincheck.checkvalue =
! 						(Datum *) palloc(sizeof(Datum));
! 					scratch->d.domaincheck.checknull =
! 						(bool *) palloc(sizeof(bool));
  				}
  
  				/*
--- 2876,2901 ----
  	{
  		DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
  
  		switch (con->constrainttype)
  		{
  			case DOM_CONSTRAINT_NOTNULL:
! 				scratch = (ExprEvalStep_domaincheck *)
! 					ExprEvalPushStep(state,
! 		       	                     EEOP_DOMAIN_NOTNULL,
! 									 resv,
! 									 resnull);
! 				scratch->resulttype = ctest->resulttype;
! 				scratch->checkvalue = NULL;
! 				scratch->checknull = NULL;
! 				scratch->constraintname = con->name;
! 
  				break;
  			case DOM_CONSTRAINT_CHECK:
  				/* Allocate workspace for CHECK output if we didn't yet */
! 				if (checkvalue == NULL)
  				{
! 					checkvalue = (Datum *) palloc(sizeof(Datum));
! 					checknull = (bool *) palloc(sizeof(bool));
  				}
  
  				/*
***************
*** 2645,2661 **** ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
  					 */
  					if (get_typlen(ctest->resulttype) == -1)
  					{
  						/* Yes, so make output workspace for MAKE_READONLY */
  						domainval = (Datum *) palloc(sizeof(Datum));
  						domainnull = (bool *) palloc(sizeof(bool));
  
  						/* Emit MAKE_READONLY */
! 						scratch2.opcode = EEOP_MAKE_READONLY;
! 						scratch2.resvalue = domainval;
! 						scratch2.resnull = domainnull;
! 						scratch2.d.make_readonly.value = resv;
! 						scratch2.d.make_readonly.isnull = resnull;
! 						ExprEvalPushStep(state, &scratch2);
  					}
  					else
  					{
--- 2910,2929 ----
  					 */
  					if (get_typlen(ctest->resulttype) == -1)
  					{
+ 						ExprEvalStep_make_readonly *scratch2;
+ 
  						/* Yes, so make output workspace for MAKE_READONLY */
  						domainval = (Datum *) palloc(sizeof(Datum));
  						domainnull = (bool *) palloc(sizeof(bool));
  
  						/* Emit MAKE_READONLY */
! 						scratch2 = (ExprEvalStep_make_readonly *)
! 							ExprEvalPushStep(state,
! 				       	                     EEOP_MAKE_READONLY,
! 											 domainval,
! 											 domainnull);
! 						scratch2->value = resv;
! 						scratch2->isnull = resnull;
  					}
  					else
  					{
***************
*** 2678,2692 **** ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
  
  				/* evaluate check expression value */
  				ExecInitExprRec(con->check_expr, parent, state,
! 								scratch->d.domaincheck.checkvalue,
! 								scratch->d.domaincheck.checknull);
  
  				state->innermost_domainval = save_innermost_domainval;
  				state->innermost_domainnull = save_innermost_domainnull;
  
  				/* now test result */
! 				scratch->opcode = EEOP_DOMAIN_CHECK;
! 				ExprEvalPushStep(state, scratch);
  
  				break;
  			default:
--- 2946,2966 ----
  
  				/* evaluate check expression value */
  				ExecInitExprRec(con->check_expr, parent, state,
! 								checkvalue, checknull);
  
  				state->innermost_domainval = save_innermost_domainval;
  				state->innermost_domainnull = save_innermost_domainnull;
  
  				/* now test result */
! 				scratch = (ExprEvalStep_domaincheck *)
! 					ExprEvalPushStep(state,
! 		       	                     EEOP_DOMAIN_CHECK,
! 									 resv,
! 									 resnull);
! 				scratch->resulttype = ctest->resulttype;
! 				scratch->checkvalue = checkvalue;
! 				scratch->checknull = checknull;
! 				scratch->constraintname = con->name;
  
  				break;
  			default:
*** a/src/backend/executor/execExprInterp.c
--- b/src/backend/executor/execExprInterp.c
***************
*** 116,128 **** static const void **dispatch_table = NULL;
  
  #define EEO_NEXT() \
  	do { \
! 		op++; \
  		EEO_DISPATCH(); \
  	} while (0)
  
! #define EEO_JUMP(stepno) \
  	do { \
! 		op = &state->steps[stepno]; \
  		EEO_DISPATCH(); \
  	} while (0)
  
--- 116,128 ----
  
  #define EEO_NEXT() \
  	do { \
! 		op = op->nextstep; \
  		EEO_DISPATCH(); \
  	} while (0)
  
! #define EEO_JUMP(step) \
  	do { \
! 		op = step; \
  		EEO_DISPATCH(); \
  	} while (0)
  
***************
*** 135,141 **** static void CheckVarSlotCompatibility(TupleTableSlot *slot, int attnum, Oid vart
  static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
  				   TupleDesc *cache_field, ExprContext *econtext);
  static void ShutdownTupleDescRef(Datum arg);
! static void ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
  				   ExprContext *econtext, bool checkisnull);
  
  /* fast-path evaluation functions */
--- 135,141 ----
  static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
  				   TupleDesc *cache_field, ExprContext *econtext);
  static void ShutdownTupleDescRef(Datum arg);
! static void ExecEvalRowNullInt(ExprState *state, ExprEvalStep_nulltest_row *sop,
  				   ExprContext *econtext, bool checkisnull);
  
  /* fast-path evaluation functions */
***************
*** 152,157 **** static Datum ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool
--- 152,163 ----
  
  
  /*
+  * Macro to define a specific operator "sop" by casting the generic "op"
+  * to the right type.
+  */
+ #define DEF_SOP(type) type *sop = (type *)op
+ 
+ /*
   * Prepare ExprState for interpreted execution.
   */
  void
***************
*** 161,168 **** ExecReadyInterpretedExpr(ExprState *state)
  	ExecInitInterpreter();
  
  	/* Simple validity checks on expression */
! 	Assert(state->steps_len >= 1);
! 	Assert(state->steps[state->steps_len - 1].opcode == EEOP_DONE);
  
  	/*
  	 * Don't perform redundant initialization. This is unreachable in current
--- 167,173 ----
  	ExecInitInterpreter();
  
  	/* Simple validity checks on expression */
! 	Assert(state->steps != NULL);
  
  	/*
  	 * Don't perform redundant initialization. This is unreachable in current
***************
*** 189,198 **** ExecReadyInterpretedExpr(ExprState *state)
  	 * enough that the overhead of ExecInterpExpr matters.  For more complex
  	 * expressions it's cheaper to use ExecInterpExpr always.
  	 */
! 	if (state->steps_len == 3)
  	{
! 		ExprEvalOp	step0 = state->steps[0].opcode;
! 		ExprEvalOp	step1 = state->steps[1].opcode;
  
  		if (step0 == EEOP_INNER_FETCHSOME &&
  			step1 == EEOP_INNER_VAR_FIRST)
--- 194,203 ----
  	 * enough that the overhead of ExecInterpExpr matters.  For more complex
  	 * expressions it's cheaper to use ExecInterpExpr always.
  	 */
! 	if (state->num_steps == 3)
  	{
! 		ExprEvalOp	step0 = state->steps->opcode;
! 		ExprEvalOp	step1 = state->steps->nextstep->opcode;
  
  		if (step0 == EEOP_INNER_FETCHSOME &&
  			step1 == EEOP_INNER_VAR_FIRST)
***************
*** 231,238 **** ExecReadyInterpretedExpr(ExprState *state)
  			return;
  		}
  	}
! 	else if (state->steps_len == 2 &&
! 			 state->steps[0].opcode == EEOP_CONST)
  	{
  		state->evalfunc = ExecJustConst;
  		return;
--- 236,243 ----
  			return;
  		}
  	}
! 	else if (state->num_steps == 2 &&
! 			 state->steps->opcode == EEOP_CONST)
  	{
  		state->evalfunc = ExecJustConst;
  		return;
***************
*** 245,258 **** ExecReadyInterpretedExpr(ExprState *state)
  	 * address to jump to.  (Use ExecEvalStepOp() to get back the opcode.)
  	 */
  	{
! 		int			off;
! 
! 		for (off = 0; off < state->steps_len; off++)
! 		{
! 			ExprEvalStep *op = &state->steps[off];
  
  			op->opcode = EEO_OPCODE(op->opcode);
- 		}
  
  		state->flags |= EEO_FLAG_DIRECT_THREADED;
  	}
--- 250,264 ----
  	 * address to jump to.  (Use ExecEvalStepOp() to get back the opcode.)
  	 */
  	{
! 		ExprEvalStep *op = state->steps;
  
+ 		/*
+ 		 * We explicitly don't stop at EEO_DONE here because we need to
+ 		 * resolve that opcode. Instead, depend on the NULL nextstep to
+ 		 * indicate the end of the list.
+ 		 */
+ 		for (op = state->steps; op != NULL; op = op->nextstep)
  			op->opcode = EEO_OPCODE(op->opcode);
  
  		state->flags |= EEO_FLAG_DIRECT_THREADED;
  	}
***************
*** 395,439 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_INNER_FETCHSOME)
  		{
  			/* XXX: worthwhile to check tts_nvalid inline first? */
! 			slot_getsomeattrs(innerslot, op->d.fetch.last_var);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_OUTER_FETCHSOME)
  		{
! 			slot_getsomeattrs(outerslot, op->d.fetch.last_var);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_SCAN_FETCHSOME)
  		{
! 			slot_getsomeattrs(scanslot, op->d.fetch.last_var);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_INNER_VAR_FIRST)
  		{
! 			int			attnum = op->d.var.attnum;
  
  			/*
  			 * First time through, check whether attribute matches Var.  Might
  			 * not be ok anymore, due to schema changes.
  			 */
! 			CheckVarSlotCompatibility(innerslot, attnum + 1, op->d.var.vartype);
  
  			/* Skip that check on subsequent evaluations */
! 			op->opcode = EEO_OPCODE(EEOP_INNER_VAR);
  
  			/* FALL THROUGH to EEOP_INNER_VAR */
  		}
  
  		EEO_CASE(EEOP_INNER_VAR)
  		{
! 			int			attnum = op->d.var.attnum;
  
  			/*
  			 * Since we already extracted all referenced columns from the
--- 401,453 ----
  
  		EEO_CASE(EEOP_INNER_FETCHSOME)
  		{
+ 			DEF_SOP(ExprEvalStep_fetch);
+ 
  			/* XXX: worthwhile to check tts_nvalid inline first? */
! 			slot_getsomeattrs(innerslot, sop->last_var);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_OUTER_FETCHSOME)
  		{
! 			DEF_SOP(ExprEvalStep_fetch);
! 
! 			slot_getsomeattrs(outerslot, sop->last_var);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_SCAN_FETCHSOME)
  		{
! 			DEF_SOP(ExprEvalStep_fetch);
! 
! 			slot_getsomeattrs(scanslot, sop->last_var);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_INNER_VAR_FIRST)
  		{
! 			DEF_SOP(ExprEvalStep_var);
! 			int			attnum = sop->attnum;
  
  			/*
  			 * First time through, check whether attribute matches Var.  Might
  			 * not be ok anymore, due to schema changes.
  			 */
! 			CheckVarSlotCompatibility(innerslot, attnum + 1, sop->vartype);
  
  			/* Skip that check on subsequent evaluations */
! 			sop->opcode = EEO_OPCODE(EEOP_INNER_VAR);
  
  			/* FALL THROUGH to EEOP_INNER_VAR */
  		}
  
  		EEO_CASE(EEOP_INNER_VAR)
  		{
! 			DEF_SOP(ExprEvalStep_var);
! 			int			attnum = sop->attnum;
  
  			/*
  			 * Since we already extracted all referenced columns from the
***************
*** 442,563 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  			 * have an Assert to check that that did happen.
  			 */
  			Assert(attnum >= 0 && attnum < innerslot->tts_nvalid);
! 			*op->resvalue = innerslot->tts_values[attnum];
! 			*op->resnull = innerslot->tts_isnull[attnum];
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_OUTER_VAR_FIRST)
  		{
! 			int			attnum = op->d.var.attnum;
  
  			/* See EEOP_INNER_VAR_FIRST comments */
  
! 			CheckVarSlotCompatibility(outerslot, attnum + 1, op->d.var.vartype);
! 			op->opcode = EEO_OPCODE(EEOP_OUTER_VAR);
  
  			/* FALL THROUGH to EEOP_OUTER_VAR */
  		}
  
  		EEO_CASE(EEOP_OUTER_VAR)
  		{
! 			int			attnum = op->d.var.attnum;
  
  			/* See EEOP_INNER_VAR comments */
  
  			Assert(attnum >= 0 && attnum < outerslot->tts_nvalid);
! 			*op->resvalue = outerslot->tts_values[attnum];
! 			*op->resnull = outerslot->tts_isnull[attnum];
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_SCAN_VAR_FIRST)
  		{
! 			int			attnum = op->d.var.attnum;
  
  			/* See EEOP_INNER_VAR_FIRST comments */
  
! 			CheckVarSlotCompatibility(scanslot, attnum + 1, op->d.var.vartype);
! 			op->opcode = EEO_OPCODE(EEOP_SCAN_VAR);
  
  			/* FALL THROUGH to EEOP_SCAN_VAR */
  		}
  
  		EEO_CASE(EEOP_SCAN_VAR)
  		{
! 			int			attnum = op->d.var.attnum;
  
  			/* See EEOP_INNER_VAR comments */
  
  			Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
! 			*op->resvalue = scanslot->tts_values[attnum];
! 			*op->resnull = scanslot->tts_isnull[attnum];
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_INNER_SYSVAR)
  		{
! 			int			attnum = op->d.var.attnum;
  
  			/* these asserts must match defenses in slot_getattr */
  			Assert(innerslot->tts_tuple != NULL);
  			Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr));
  			/* heap_getsysattr has sufficient defenses against bad attnums */
  
! 			*op->resvalue = heap_getsysattr(innerslot->tts_tuple, attnum,
  											innerslot->tts_tupleDescriptor,
! 											op->resnull);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_OUTER_SYSVAR)
  		{
! 			int			attnum = op->d.var.attnum;
  
  			/* these asserts must match defenses in slot_getattr */
  			Assert(outerslot->tts_tuple != NULL);
  			Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr));
  
  			/* heap_getsysattr has sufficient defenses against bad attnums */
! 			*op->resvalue = heap_getsysattr(outerslot->tts_tuple, attnum,
  											outerslot->tts_tupleDescriptor,
! 											op->resnull);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_SCAN_SYSVAR)
  		{
! 			int			attnum = op->d.var.attnum;
  
  			/* these asserts must match defenses in slot_getattr */
  			Assert(scanslot->tts_tuple != NULL);
  			Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr));
  			/* heap_getsysattr has sufficient defenses against bad attnums */
  
! 			*op->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum,
  											scanslot->tts_tupleDescriptor,
! 											op->resnull);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_WHOLEROW)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalWholeRowVar(state, op, econtext);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_ASSIGN_INNER_VAR)
  		{
! 			int			resultnum = op->d.assign_var.resultnum;
! 			int			attnum = op->d.assign_var.attnum;
  
  			/*
  			 * We do not need CheckVarSlotCompatibility here; that was taken
--- 456,588 ----
  			 * have an Assert to check that that did happen.
  			 */
  			Assert(attnum >= 0 && attnum < innerslot->tts_nvalid);
! 			*sop->resvalue = innerslot->tts_values[attnum];
! 			*sop->resnull = innerslot->tts_isnull[attnum];
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_OUTER_VAR_FIRST)
  		{
! 			DEF_SOP(ExprEvalStep_var);
! 			int			attnum = sop->attnum;
  
  			/* See EEOP_INNER_VAR_FIRST comments */
  
! 			CheckVarSlotCompatibility(outerslot, attnum + 1, sop->vartype);
! 			sop->opcode = EEO_OPCODE(EEOP_OUTER_VAR);
  
  			/* FALL THROUGH to EEOP_OUTER_VAR */
  		}
  
  		EEO_CASE(EEOP_OUTER_VAR)
  		{
! 			DEF_SOP(ExprEvalStep_var);
! 			int			attnum = sop->attnum;
  
  			/* See EEOP_INNER_VAR comments */
  
  			Assert(attnum >= 0 && attnum < outerslot->tts_nvalid);
! 			*sop->resvalue = outerslot->tts_values[attnum];
! 			*sop->resnull = outerslot->tts_isnull[attnum];
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_SCAN_VAR_FIRST)
  		{
! 			DEF_SOP(ExprEvalStep_var);
! 			int			attnum = sop->attnum;
  
  			/* See EEOP_INNER_VAR_FIRST comments */
  
! 			CheckVarSlotCompatibility(scanslot, attnum + 1, sop->vartype);
! 			sop->opcode = EEO_OPCODE(EEOP_SCAN_VAR);
  
  			/* FALL THROUGH to EEOP_SCAN_VAR */
  		}
  
  		EEO_CASE(EEOP_SCAN_VAR)
  		{
! 			DEF_SOP(ExprEvalStep_var);
! 			int			attnum = sop->attnum;
  
  			/* See EEOP_INNER_VAR comments */
  
  			Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
! 			*sop->resvalue = scanslot->tts_values[attnum];
! 			*sop->resnull = scanslot->tts_isnull[attnum];
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_INNER_SYSVAR)
  		{
! 			DEF_SOP(ExprEvalStep_var);
! 			int			attnum = sop->attnum;
  
  			/* these asserts must match defenses in slot_getattr */
  			Assert(innerslot->tts_tuple != NULL);
  			Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr));
  			/* heap_getsysattr has sufficient defenses against bad attnums */
  
! 			*sop->resvalue = heap_getsysattr(innerslot->tts_tuple, attnum,
  											innerslot->tts_tupleDescriptor,
! 											sop->resnull);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_OUTER_SYSVAR)
  		{
! 			DEF_SOP(ExprEvalStep_var);
! 			int			attnum = sop->attnum;
  
  			/* these asserts must match defenses in slot_getattr */
  			Assert(outerslot->tts_tuple != NULL);
  			Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr));
  
  			/* heap_getsysattr has sufficient defenses against bad attnums */
! 			*sop->resvalue = heap_getsysattr(outerslot->tts_tuple, attnum,
  											outerslot->tts_tupleDescriptor,
! 											sop->resnull);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_SCAN_SYSVAR)
  		{
! 			DEF_SOP(ExprEvalStep_var);
! 			int			attnum = sop->attnum;
  
  			/* these asserts must match defenses in slot_getattr */
  			Assert(scanslot->tts_tuple != NULL);
  			Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr));
  			/* heap_getsysattr has sufficient defenses against bad attnums */
  
! 			*sop->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum,
  											scanslot->tts_tupleDescriptor,
! 											sop->resnull);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_WHOLEROW)
  		{
+ 			DEF_SOP(ExprEvalStep_wholerow);
+ 
  			/* too complex for an inline implementation */
! 			ExecEvalWholeRowVar(state, sop, econtext);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_ASSIGN_INNER_VAR)
  		{
! 			DEF_SOP(ExprEvalStep_assign_var);
! 
! 			int			resultnum = sop->resultnum;
! 			int			attnum = sop->attnum;
  
  			/*
  			 * We do not need CheckVarSlotCompatibility here; that was taken
***************
*** 572,579 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_ASSIGN_OUTER_VAR)
  		{
! 			int			resultnum = op->d.assign_var.resultnum;
! 			int			attnum = op->d.assign_var.attnum;
  
  			/*
  			 * We do not need CheckVarSlotCompatibility here; that was taken
--- 597,605 ----
  
  		EEO_CASE(EEOP_ASSIGN_OUTER_VAR)
  		{
! 			DEF_SOP(ExprEvalStep_assign_var);
! 			int			resultnum = sop->resultnum;
! 			int			attnum = sop->attnum;
  
  			/*
  			 * We do not need CheckVarSlotCompatibility here; that was taken
***************
*** 588,595 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_ASSIGN_SCAN_VAR)
  		{
! 			int			resultnum = op->d.assign_var.resultnum;
! 			int			attnum = op->d.assign_var.attnum;
  
  			/*
  			 * We do not need CheckVarSlotCompatibility here; that was taken
--- 614,622 ----
  
  		EEO_CASE(EEOP_ASSIGN_SCAN_VAR)
  		{
! 			DEF_SOP(ExprEvalStep_assign_var);
! 			int			resultnum = sop->resultnum;
! 			int			attnum = sop->attnum;
  
  			/*
  			 * We do not need CheckVarSlotCompatibility here; that was taken
***************
*** 604,610 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_ASSIGN_TMP)
  		{
! 			int			resultnum = op->d.assign_tmp.resultnum;
  
  			resultslot->tts_values[resultnum] = state->resvalue;
  			resultslot->tts_isnull[resultnum] = state->resnull;
--- 631,638 ----
  
  		EEO_CASE(EEOP_ASSIGN_TMP)
  		{
! 			DEF_SOP(ExprEvalStep_assign_tmp);
! 			int			resultnum = sop->resultnum;
  
  			resultslot->tts_values[resultnum] = state->resvalue;
  			resultslot->tts_isnull[resultnum] = state->resnull;
***************
*** 614,620 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_ASSIGN_TMP_MAKE_RO)
  		{
! 			int			resultnum = op->d.assign_tmp.resultnum;
  
  			resultslot->tts_isnull[resultnum] = state->resnull;
  			if (!resultslot->tts_isnull[resultnum])
--- 642,649 ----
  
  		EEO_CASE(EEOP_ASSIGN_TMP_MAKE_RO)
  		{
! 			DEF_SOP(ExprEvalStep_assign_tmp);
! 			int			resultnum = sop->resultnum;
  
  			resultslot->tts_isnull[resultnum] = state->resnull;
  			if (!resultslot->tts_isnull[resultnum])
***************
*** 628,635 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_CONST)
  		{
! 			*op->resnull = op->d.constval.isnull;
! 			*op->resvalue = op->d.constval.value;
  
  			EEO_NEXT();
  		}
--- 657,666 ----
  
  		EEO_CASE(EEOP_CONST)
  		{
! 			DEF_SOP(ExprEvalStep_constval);
! 
! 			*sop->resnull = sop->isnull;
! 			*sop->resvalue = sop->value;
  
  			EEO_NEXT();
  		}
***************
*** 644,676 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  		 */
  		EEO_CASE(EEOP_FUNCEXPR)
  		{
! 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
  
  			fcinfo->isnull = false;
! 			*op->resvalue = (op->d.func.fn_addr) (fcinfo);
! 			*op->resnull = fcinfo->isnull;
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_FUNCEXPR_STRICT)
  		{
! 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
  			bool	   *argnull = fcinfo->argnull;
  			int			argno;
  
  			/* strict function, so check for NULL args */
! 			for (argno = 0; argno < op->d.func.nargs; argno++)
  			{
  				if (argnull[argno])
  				{
! 					*op->resnull = true;
  					goto strictfail;
  				}
  			}
  			fcinfo->isnull = false;
! 			*op->resvalue = (op->d.func.fn_addr) (fcinfo);
! 			*op->resnull = fcinfo->isnull;
  
  	strictfail:
  			EEO_NEXT();
--- 675,709 ----
  		 */
  		EEO_CASE(EEOP_FUNCEXPR)
  		{
! 			DEF_SOP(ExprEvalStep_func);
! 			FunctionCallInfo fcinfo = sop->fcinfo_data;
  
  			fcinfo->isnull = false;
! 			*sop->resvalue = (sop->fn_addr) (fcinfo);
! 			*sop->resnull = fcinfo->isnull;
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_FUNCEXPR_STRICT)
  		{
! 			DEF_SOP(ExprEvalStep_func);
! 			FunctionCallInfo fcinfo = sop->fcinfo_data;
  			bool	   *argnull = fcinfo->argnull;
  			int			argno;
  
  			/* strict function, so check for NULL args */
! 			for (argno = 0; argno < sop->nargs; argno++)
  			{
  				if (argnull[argno])
  				{
! 					*sop->resnull = true;
  					goto strictfail;
  				}
  			}
  			fcinfo->isnull = false;
! 			*sop->resvalue = (sop->fn_addr) (fcinfo);
! 			*sop->resnull = fcinfo->isnull;
  
  	strictfail:
  			EEO_NEXT();
***************
*** 678,691 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_FUNCEXPR_FUSAGE)
  		{
! 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
  			PgStat_FunctionCallUsage fcusage;
  
  			pgstat_init_function_usage(fcinfo, &fcusage);
  
  			fcinfo->isnull = false;
! 			*op->resvalue = (op->d.func.fn_addr) (fcinfo);
! 			*op->resnull = fcinfo->isnull;
  
  			pgstat_end_function_usage(&fcusage, true);
  
--- 711,725 ----
  
  		EEO_CASE(EEOP_FUNCEXPR_FUSAGE)
  		{
! 			DEF_SOP(ExprEvalStep_func);
! 			FunctionCallInfo fcinfo = sop->fcinfo_data;
  			PgStat_FunctionCallUsage fcusage;
  
  			pgstat_init_function_usage(fcinfo, &fcusage);
  
  			fcinfo->isnull = false;
! 			*sop->resvalue = (sop->fn_addr) (fcinfo);
! 			*sop->resnull = fcinfo->isnull;
  
  			pgstat_end_function_usage(&fcusage, true);
  
***************
*** 694,710 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_FUNCEXPR_STRICT_FUSAGE)
  		{
! 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
  			PgStat_FunctionCallUsage fcusage;
  			bool	   *argnull = fcinfo->argnull;
  			int			argno;
  
  			/* strict function, so check for NULL args */
! 			for (argno = 0; argno < op->d.func.nargs; argno++)
  			{
  				if (argnull[argno])
  				{
! 					*op->resnull = true;
  					goto strictfail_fusage;
  				}
  			}
--- 728,745 ----
  
  		EEO_CASE(EEOP_FUNCEXPR_STRICT_FUSAGE)
  		{
! 			DEF_SOP(ExprEvalStep_func);
! 			FunctionCallInfo fcinfo = sop->fcinfo_data;
  			PgStat_FunctionCallUsage fcusage;
  			bool	   *argnull = fcinfo->argnull;
  			int			argno;
  
  			/* strict function, so check for NULL args */
! 			for (argno = 0; argno < sop->nargs; argno++)
  			{
  				if (argnull[argno])
  				{
! 					*sop->resnull = true;
  					goto strictfail_fusage;
  				}
  			}
***************
*** 712,719 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  			pgstat_init_function_usage(fcinfo, &fcusage);
  
  			fcinfo->isnull = false;
! 			*op->resvalue = (op->d.func.fn_addr) (fcinfo);
! 			*op->resnull = fcinfo->isnull;
  
  			pgstat_end_function_usage(&fcusage, true);
  
--- 747,754 ----
  			pgstat_init_function_usage(fcinfo, &fcusage);
  
  			fcinfo->isnull = false;
! 			*sop->resvalue = (sop->fn_addr) (fcinfo);
! 			*sop->resnull = fcinfo->isnull;
  
  			pgstat_end_function_usage(&fcusage, true);
  
***************
*** 733,739 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  		 */
  		EEO_CASE(EEOP_BOOL_AND_STEP_FIRST)
  		{
! 			*op->d.boolexpr.anynull = false;
  
  			/*
  			 * EEOP_BOOL_AND_STEP_FIRST resets anynull, otherwise it's the
--- 768,776 ----
  		 */
  		EEO_CASE(EEOP_BOOL_AND_STEP_FIRST)
  		{
! 			DEF_SOP(ExprEvalStep_boolexpr);
! 
! 			*sop->anynull = false;
  
  			/*
  			 * EEOP_BOOL_AND_STEP_FIRST resets anynull, otherwise it's the
***************
*** 745,759 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_BOOL_AND_STEP)
  		{
! 			if (*op->resnull)
  			{
! 				*op->d.boolexpr.anynull = true;
  			}
! 			else if (!DatumGetBool(*op->resvalue))
  			{
  				/* result is already set to FALSE, need not change it */
  				/* bail out early */
! 				EEO_JUMP(op->d.boolexpr.jumpdone);
  			}
  
  			EEO_NEXT();
--- 782,798 ----
  
  		EEO_CASE(EEOP_BOOL_AND_STEP)
  		{
! 			DEF_SOP(ExprEvalStep_boolexpr);
! 
! 			if (*sop->resnull)
  			{
! 				*sop->anynull = true;
  			}
! 			else if (!DatumGetBool(*sop->resvalue))
  			{
  				/* result is already set to FALSE, need not change it */
  				/* bail out early */
! 				EEO_JUMP(sop->jumpdone);
  			}
  
  			EEO_NEXT();
***************
*** 761,771 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_BOOL_AND_STEP_LAST)
  		{
! 			if (*op->resnull)
  			{
  				/* result is already set to NULL, need not change it */
  			}
! 			else if (!DatumGetBool(*op->resvalue))
  			{
  				/* result is already set to FALSE, need not change it */
  
--- 800,812 ----
  
  		EEO_CASE(EEOP_BOOL_AND_STEP_LAST)
  		{
! 			DEF_SOP(ExprEvalStep_boolexpr);
! 
! 			if (*sop->resnull)
  			{
  				/* result is already set to NULL, need not change it */
  			}
! 			else if (!DatumGetBool(*sop->resvalue))
  			{
  				/* result is already set to FALSE, need not change it */
  
***************
*** 775,784 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  				 * except more expensive.
  				 */
  			}
! 			else if (*op->d.boolexpr.anynull)
  			{
! 				*op->resvalue = (Datum) 0;
! 				*op->resnull = true;
  			}
  			else
  			{
--- 816,825 ----
  				 * except more expensive.
  				 */
  			}
! 			else if (*sop->anynull)
  			{
! 				*sop->resvalue = (Datum) 0;
! 				*sop->resnull = true;
  			}
  			else
  			{
***************
*** 800,806 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  		 */
  		EEO_CASE(EEOP_BOOL_OR_STEP_FIRST)
  		{
! 			*op->d.boolexpr.anynull = false;
  
  			/*
  			 * EEOP_BOOL_OR_STEP_FIRST resets anynull, otherwise it's the same
--- 841,849 ----
  		 */
  		EEO_CASE(EEOP_BOOL_OR_STEP_FIRST)
  		{
! 			DEF_SOP(ExprEvalStep_boolexpr);
! 
! 			*sop->anynull = false;
  
  			/*
  			 * EEOP_BOOL_OR_STEP_FIRST resets anynull, otherwise it's the same
***************
*** 812,826 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_BOOL_OR_STEP)
  		{
! 			if (*op->resnull)
  			{
! 				*op->d.boolexpr.anynull = true;
  			}
! 			else if (DatumGetBool(*op->resvalue))
  			{
  				/* result is already set to TRUE, need not change it */
  				/* bail out early */
! 				EEO_JUMP(op->d.boolexpr.jumpdone);
  			}
  
  			EEO_NEXT();
--- 855,871 ----
  
  		EEO_CASE(EEOP_BOOL_OR_STEP)
  		{
! 			DEF_SOP(ExprEvalStep_boolexpr);
! 
! 			if (*sop->resnull)
  			{
! 				*sop->anynull = true;
  			}
! 			else if (DatumGetBool(*sop->resvalue))
  			{
  				/* result is already set to TRUE, need not change it */
  				/* bail out early */
! 				EEO_JUMP(sop->jumpdone);
  			}
  
  			EEO_NEXT();
***************
*** 828,838 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_BOOL_OR_STEP_LAST)
  		{
! 			if (*op->resnull)
  			{
  				/* result is already set to NULL, need not change it */
  			}
! 			else if (DatumGetBool(*op->resvalue))
  			{
  				/* result is already set to TRUE, need not change it */
  
--- 873,885 ----
  
  		EEO_CASE(EEOP_BOOL_OR_STEP_LAST)
  		{
! 			DEF_SOP(ExprEvalStep_boolexpr);
! 
! 			if (*sop->resnull)
  			{
  				/* result is already set to NULL, need not change it */
  			}
! 			else if (DatumGetBool(*sop->resvalue))
  			{
  				/* result is already set to TRUE, need not change it */
  
***************
*** 842,851 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  				 * more expensive.
  				 */
  			}
! 			else if (*op->d.boolexpr.anynull)
  			{
! 				*op->resvalue = (Datum) 0;
! 				*op->resnull = true;
  			}
  			else
  			{
--- 889,898 ----
  				 * more expensive.
  				 */
  			}
! 			else if (*sop->anynull)
  			{
! 				*sop->resvalue = (Datum) 0;
! 				*sop->resnull = true;
  			}
  			else
  			{
***************
*** 857,885 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_BOOL_NOT_STEP)
  		{
  			/*
  			 * Evaluation of 'not' is simple... if expr is false, then return
  			 * 'true' and vice versa.  It's safe to do this even on a
  			 * nominally null value, so we ignore resnull; that means that
  			 * NULL in produces NULL out, which is what we want.
  			 */
! 			*op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_QUAL)
  		{
  			/* simplified version of BOOL_AND_STEP for use by ExecQual() */
  
  			/* If argument (also result) is false or null ... */
! 			if (*op->resnull ||
! 				!DatumGetBool(*op->resvalue))
  			{
  				/* ... bail out early, returning FALSE */
! 				*op->resnull = false;
! 				*op->resvalue = BoolGetDatum(false);
! 				EEO_JUMP(op->d.qualexpr.jumpdone);
  			}
  
  			/*
--- 904,936 ----
  
  		EEO_CASE(EEOP_BOOL_NOT_STEP)
  		{
+ 			DEF_SOP(ExprEvalStep_boolexpr);
+ 
  			/*
  			 * Evaluation of 'not' is simple... if expr is false, then return
  			 * 'true' and vice versa.  It's safe to do this even on a
  			 * nominally null value, so we ignore resnull; that means that
  			 * NULL in produces NULL out, which is what we want.
  			 */
! 			*sop->resvalue = BoolGetDatum(!DatumGetBool(*sop->resvalue));
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_QUAL)
  		{
+ 			DEF_SOP(ExprEvalStep_qualexpr);
+ 
  			/* simplified version of BOOL_AND_STEP for use by ExecQual() */
  
  			/* If argument (also result) is false or null ... */
! 			if (*sop->resnull ||
! 				!DatumGetBool(*sop->resvalue))
  			{
  				/* ... bail out early, returning FALSE */
! 				*sop->resnull = false;
! 				*sop->resvalue = BoolGetDatum(false);
! 				EEO_JUMP(sop->jumpdone);
  			}
  
  			/*
***************
*** 892,956 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_JUMP)
  		{
  			/* Unconditionally jump to target step */
! 			EEO_JUMP(op->d.jump.jumpdone);
  		}
  
  		EEO_CASE(EEOP_JUMP_IF_NULL)
  		{
  			/* Transfer control if current result is null */
! 			if (*op->resnull)
! 				EEO_JUMP(op->d.jump.jumpdone);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_JUMP_IF_NOT_NULL)
  		{
  			/* Transfer control if current result is non-null */
! 			if (!*op->resnull)
! 				EEO_JUMP(op->d.jump.jumpdone);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_JUMP_IF_NOT_TRUE)
  		{
  			/* Transfer control if current result is null or false */
! 			if (*op->resnull || !DatumGetBool(*op->resvalue))
! 				EEO_JUMP(op->d.jump.jumpdone);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_NULLTEST_ISNULL)
  		{
! 			*op->resvalue = BoolGetDatum(*op->resnull);
! 			*op->resnull = false;
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_NULLTEST_ISNOTNULL)
  		{
! 			*op->resvalue = BoolGetDatum(!*op->resnull);
! 			*op->resnull = false;
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_NULLTEST_ROWISNULL)
  		{
  			/* out of line implementation: too large */
! 			ExecEvalRowNull(state, op, econtext);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_NULLTEST_ROWISNOTNULL)
  		{
  			/* out of line implementation: too large */
! 			ExecEvalRowNotNull(state, op, econtext);
  
  			EEO_NEXT();
  		}
--- 943,1023 ----
  
  		EEO_CASE(EEOP_JUMP)
  		{
+ 			DEF_SOP(ExprEvalStep_jump);
+ 
  			/* Unconditionally jump to target step */
! 			EEO_JUMP(sop->jumpdone);
  		}
  
  		EEO_CASE(EEOP_JUMP_IF_NULL)
  		{
+ 			DEF_SOP(ExprEvalStep_jump);
+ 
  			/* Transfer control if current result is null */
! 			if (*sop->resnull)
! 				EEO_JUMP(sop->jumpdone);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_JUMP_IF_NOT_NULL)
  		{
+ 			DEF_SOP(ExprEvalStep_jump);
+ 
  			/* Transfer control if current result is non-null */
! 			if (!*sop->resnull)
! 				EEO_JUMP(sop->jumpdone);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_JUMP_IF_NOT_TRUE)
  		{
+ 			DEF_SOP(ExprEvalStep_jump);
+ 
  			/* Transfer control if current result is null or false */
! 			if (*sop->resnull || !DatumGetBool(*sop->resvalue))
! 				EEO_JUMP(sop->jumpdone);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_NULLTEST_ISNULL)
  		{
! 			DEF_SOP(ExprEvalStep_nulltest_row);
! 
! 			*sop->resvalue = BoolGetDatum(*sop->resnull);
! 			*sop->resnull = false;
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_NULLTEST_ISNOTNULL)
  		{
! 			DEF_SOP(ExprEvalStep_nulltest_row);
! 
! 			*sop->resvalue = BoolGetDatum(!*sop->resnull);
! 			*sop->resnull = false;
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_NULLTEST_ROWISNULL)
  		{
+ 			DEF_SOP(ExprEvalStep_nulltest_row);
+ 
  			/* out of line implementation: too large */
! 			ExecEvalRowNull(state, sop, econtext);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_NULLTEST_ROWISNOTNULL)
  		{
+ 			DEF_SOP(ExprEvalStep_nulltest_row);
+ 
  			/* out of line implementation: too large */
! 			ExecEvalRowNotNull(state, sop, econtext);
  
  			EEO_NEXT();
  		}
***************
*** 959,968 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_BOOLTEST_IS_TRUE)
  		{
! 			if (*op->resnull)
  			{
! 				*op->resvalue = BoolGetDatum(false);
! 				*op->resnull = false;
  			}
  			/* else, input value is the correct output as well */
  
--- 1026,1037 ----
  
  		EEO_CASE(EEOP_BOOLTEST_IS_TRUE)
  		{
! 			DEF_SOP(ExprEvalStep);
! 
! 			if (*sop->resnull)
  			{
! 				*sop->resvalue = BoolGetDatum(false);
! 				*sop->resnull = false;
  			}
  			/* else, input value is the correct output as well */
  
***************
*** 971,1006 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_BOOLTEST_IS_NOT_TRUE)
  		{
! 			if (*op->resnull)
  			{
! 				*op->resvalue = BoolGetDatum(true);
! 				*op->resnull = false;
  			}
  			else
! 				*op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_BOOLTEST_IS_FALSE)
  		{
! 			if (*op->resnull)
  			{
! 				*op->resvalue = BoolGetDatum(false);
! 				*op->resnull = false;
  			}
  			else
! 				*op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_BOOLTEST_IS_NOT_FALSE)
  		{
! 			if (*op->resnull)
  			{
! 				*op->resvalue = BoolGetDatum(true);
! 				*op->resnull = false;
  			}
  			/* else, input value is the correct output as well */
  
--- 1040,1081 ----
  
  		EEO_CASE(EEOP_BOOLTEST_IS_NOT_TRUE)
  		{
! 			DEF_SOP(ExprEvalStep);
! 
! 			if (*sop->resnull)
  			{
! 				*sop->resvalue = BoolGetDatum(true);
! 				*sop->resnull = false;
  			}
  			else
! 				*sop->resvalue = BoolGetDatum(!DatumGetBool(*sop->resvalue));
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_BOOLTEST_IS_FALSE)
  		{
! 			DEF_SOP(ExprEvalStep);
! 
! 			if (*sop->resnull)
  			{
! 				*sop->resvalue = BoolGetDatum(false);
! 				*sop->resnull = false;
  			}
  			else
! 				*sop->resvalue = BoolGetDatum(!DatumGetBool(*sop->resvalue));
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_BOOLTEST_IS_NOT_FALSE)
  		{
! 			DEF_SOP(ExprEvalStep);
! 
! 			if (*sop->resnull)
  			{
! 				*sop->resvalue = BoolGetDatum(true);
! 				*sop->resnull = false;
  			}
  			/* else, input value is the correct output as well */
  
***************
*** 1009,1029 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_PARAM_EXEC)
  		{
  			/* out of line implementation: too large */
! 			ExecEvalParamExec(state, op, econtext);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_PARAM_EXTERN)
  		{
  			/* out of line implementation: too large */
! 			ExecEvalParamExtern(state, op, econtext);
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_CASE_TESTVAL)
  		{
  			/*
  			 * Normally upper parts of the expression tree have setup the
  			 * values to be returned here, but some parts of the system
--- 1084,1110 ----
  
  		EEO_CASE(EEOP_PARAM_EXEC)
  		{
+ 			DEF_SOP(ExprEvalStep_param);
+ 
  			/* out of line implementation: too large */
! 			ExecEvalParamExec(state, sop, econtext);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_PARAM_EXTERN)
  		{
+ 			DEF_SOP(ExprEvalStep_param);
+ 
  			/* out of line implementation: too large */
! 			ExecEvalParamExtern(state, sop, econtext);
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_CASE_TESTVAL)
  		{
+ 			DEF_SOP(ExprEvalStep_casetest);
+ 
  			/*
  			 * Normally upper parts of the expression tree have setup the
  			 * values to be returned here, but some parts of the system
***************
*** 1033,1047 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  			 * and this is unlikely to be performance sensitive enough to
  			 * worry about an extra branch.
  			 */
! 			if (op->d.casetest.value)
  			{
! 				*op->resvalue = *op->d.casetest.value;
! 				*op->resnull = *op->d.casetest.isnull;
  			}
  			else
  			{
! 				*op->resvalue = econtext->caseValue_datum;
! 				*op->resnull = econtext->caseValue_isNull;
  			}
  
  			EEO_NEXT();
--- 1114,1128 ----
  			 * and this is unlikely to be performance sensitive enough to
  			 * worry about an extra branch.
  			 */
! 			if (sop->value)
  			{
! 				*sop->resvalue = *sop->value;
! 				*sop->resnull = *sop->isnull;
  			}
  			else
  			{
! 				*sop->resvalue = econtext->caseValue_datum;
! 				*sop->resnull = econtext->caseValue_isNull;
  			}
  
  			EEO_NEXT();
***************
*** 1049,1066 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_DOMAIN_TESTVAL)
  		{
  			/*
  			 * See EEOP_CASE_TESTVAL comment.
  			 */
! 			if (op->d.casetest.value)
  			{
! 				*op->resvalue = *op->d.casetest.value;
! 				*op->resnull = *op->d.casetest.isnull;
  			}
  			else
  			{
! 				*op->resvalue = econtext->domainValue_datum;
! 				*op->resnull = econtext->domainValue_isNull;
  			}
  
  			EEO_NEXT();
--- 1130,1149 ----
  
  		EEO_CASE(EEOP_DOMAIN_TESTVAL)
  		{
+ 			DEF_SOP(ExprEvalStep_casetest);
+ 
  			/*
  			 * See EEOP_CASE_TESTVAL comment.
  			 */
! 			if (sop->value)
  			{
! 				*sop->resvalue = *sop->value;
! 				*sop->resnull = *sop->isnull;
  			}
  			else
  			{
! 				*sop->resvalue = econtext->domainValue_datum;
! 				*sop->resnull = econtext->domainValue_isNull;
  			}
  
  			EEO_NEXT();
***************
*** 1068,1086 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_MAKE_READONLY)
  		{
  			/*
  			 * Force a varlena value that might be read multiple times to R/O
  			 */
! 			if (!*op->d.make_readonly.isnull)
! 				*op->resvalue =
! 					MakeExpandedObjectReadOnlyInternal(*op->d.make_readonly.value);
! 			*op->resnull = *op->d.make_readonly.isnull;
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_IOCOERCE)
  		{
  			/*
  			 * Evaluate a CoerceViaIO node.  This can be quite a hot path, so
  			 * inline as much work as possible.  The source value is in our
--- 1151,1173 ----
  
  		EEO_CASE(EEOP_MAKE_READONLY)
  		{
+ 			DEF_SOP(ExprEvalStep_make_readonly);
+ 
  			/*
  			 * Force a varlena value that might be read multiple times to R/O
  			 */
! 			if (!*sop->isnull)
! 				*sop->resvalue =
! 					MakeExpandedObjectReadOnlyInternal(*sop->value);
! 			*sop->resnull = *sop->isnull;
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_IOCOERCE)
  		{
+ 			DEF_SOP(ExprEvalStep_iocoerce);
+ 
  			/*
  			 * Evaluate a CoerceViaIO node.  This can be quite a hot path, so
  			 * inline as much work as possible.  The source value is in our
***************
*** 1089,1095 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  			char	   *str;
  
  			/* call output function (similar to OutputFunctionCall) */
! 			if (*op->resnull)
  			{
  				/* output functions are not called on nulls */
  				str = NULL;
--- 1176,1182 ----
  			char	   *str;
  
  			/* call output function (similar to OutputFunctionCall) */
! 			if (*sop->resnull)
  			{
  				/* output functions are not called on nulls */
  				str = NULL;
***************
*** 1098,1105 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  			{
  				FunctionCallInfo fcinfo_out;
  
! 				fcinfo_out = op->d.iocoerce.fcinfo_data_out;
! 				fcinfo_out->arg[0] = *op->resvalue;
  				fcinfo_out->argnull[0] = false;
  
  				fcinfo_out->isnull = false;
--- 1185,1192 ----
  			{
  				FunctionCallInfo fcinfo_out;
  
! 				fcinfo_out = sop->fcinfo_data_out;
! 				fcinfo_out->arg[0] = *sop->resvalue;
  				fcinfo_out->argnull[0] = false;
  
  				fcinfo_out->isnull = false;
***************
*** 1110,1136 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  			}
  
  			/* call input function (similar to InputFunctionCall) */
! 			if (!op->d.iocoerce.finfo_in->fn_strict || str != NULL)
  			{
  				FunctionCallInfo fcinfo_in;
  
! 				fcinfo_in = op->d.iocoerce.fcinfo_data_in;
  				fcinfo_in->arg[0] = PointerGetDatum(str);
! 				fcinfo_in->argnull[0] = *op->resnull;
  				/* second and third arguments are already set up */
  
  				fcinfo_in->isnull = false;
! 				*op->resvalue = FunctionCallInvoke(fcinfo_in);
  
  				/* Should get null result if and only if str is NULL */
  				if (str == NULL)
  				{
! 					Assert(*op->resnull);
  					Assert(fcinfo_in->isnull);
  				}
  				else
  				{
! 					Assert(!*op->resnull);
  					Assert(!fcinfo_in->isnull);
  				}
  			}
--- 1197,1223 ----
  			}
  
  			/* call input function (similar to InputFunctionCall) */
! 			if (!sop->finfo_in->fn_strict || str != NULL)
  			{
  				FunctionCallInfo fcinfo_in;
  
! 				fcinfo_in = sop->fcinfo_data_in;
  				fcinfo_in->arg[0] = PointerGetDatum(str);
! 				fcinfo_in->argnull[0] = *sop->resnull;
  				/* second and third arguments are already set up */
  
  				fcinfo_in->isnull = false;
! 				*sop->resvalue = FunctionCallInvoke(fcinfo_in);
  
  				/* Should get null result if and only if str is NULL */
  				if (str == NULL)
  				{
! 					Assert(*sop->resnull);
  					Assert(fcinfo_in->isnull);
  				}
  				else
  				{
! 					Assert(!*sop->resnull);
  					Assert(!fcinfo_in->isnull);
  				}
  			}
***************
*** 1140,1145 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
--- 1227,1234 ----
  
  		EEO_CASE(EEOP_DISTINCT)
  		{
+ 			DEF_SOP(ExprEvalStep_func);
+ 
  			/*
  			 * IS DISTINCT FROM must evaluate arguments (already done into
  			 * fcinfo->arg/argnull) to determine whether they are NULL; if
***************
*** 1149,1168 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  			 * care whether that function is strict.  Because the handling of
  			 * nulls is different, we can't just reuse EEOP_FUNCEXPR.
  			 */
! 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
  
  			/* check function arguments for NULLness */
  			if (fcinfo->argnull[0] && fcinfo->argnull[1])
  			{
  				/* Both NULL? Then is not distinct... */
! 				*op->resvalue = BoolGetDatum(false);
! 				*op->resnull = false;
  			}
  			else if (fcinfo->argnull[0] || fcinfo->argnull[1])
  			{
  				/* Only one is NULL? Then is distinct... */
! 				*op->resvalue = BoolGetDatum(true);
! 				*op->resnull = false;
  			}
  			else
  			{
--- 1238,1257 ----
  			 * care whether that function is strict.  Because the handling of
  			 * nulls is different, we can't just reuse EEOP_FUNCEXPR.
  			 */
! 			FunctionCallInfo fcinfo = sop->fcinfo_data;
  
  			/* check function arguments for NULLness */
  			if (fcinfo->argnull[0] && fcinfo->argnull[1])
  			{
  				/* Both NULL? Then is not distinct... */
! 				*sop->resvalue = BoolGetDatum(false);
! 				*sop->resnull = false;
  			}
  			else if (fcinfo->argnull[0] || fcinfo->argnull[1])
  			{
  				/* Only one is NULL? Then is distinct... */
! 				*sop->resvalue = BoolGetDatum(true);
! 				*sop->resnull = false;
  			}
  			else
  			{
***************
*** 1170,1179 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  				Datum		eqresult;
  
  				fcinfo->isnull = false;
! 				eqresult = (op->d.func.fn_addr) (fcinfo);
  				/* Must invert result of "="; safe to do even if null */
! 				*op->resvalue = BoolGetDatum(!DatumGetBool(eqresult));
! 				*op->resnull = fcinfo->isnull;
  			}
  
  			EEO_NEXT();
--- 1259,1268 ----
  				Datum		eqresult;
  
  				fcinfo->isnull = false;
! 				eqresult = (sop->fn_addr) (fcinfo);
  				/* Must invert result of "="; safe to do even if null */
! 				*sop->resvalue = BoolGetDatum(!DatumGetBool(eqresult));
! 				*sop->resnull = fcinfo->isnull;
  			}
  
  			EEO_NEXT();
***************
*** 1181,1190 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_NULLIF)
  		{
  			/*
  			 * The arguments are already evaluated into fcinfo->arg/argnull.
  			 */
! 			FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
  
  			/* if either argument is NULL they can't be equal */
  			if (!fcinfo->argnull[0] && !fcinfo->argnull[1])
--- 1270,1281 ----
  
  		EEO_CASE(EEOP_NULLIF)
  		{
+ 			DEF_SOP(ExprEvalStep_func);
+ 
  			/*
  			 * The arguments are already evaluated into fcinfo->arg/argnull.
  			 */
! 			FunctionCallInfo fcinfo = sop->fcinfo_data;
  
  			/* if either argument is NULL they can't be equal */
  			if (!fcinfo->argnull[0] && !fcinfo->argnull[1])
***************
*** 1192,1298 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  				Datum		result;
  
  				fcinfo->isnull = false;
! 				result = (op->d.func.fn_addr) (fcinfo);
  
  				/* if the arguments are equal return null */
  				if (!fcinfo->isnull && DatumGetBool(result))
  				{
! 					*op->resvalue = (Datum) 0;
! 					*op->resnull = true;
  
  					EEO_NEXT();
  				}
  			}
  
  			/* Arguments aren't equal, so return the first one */
! 			*op->resvalue = fcinfo->arg[0];
! 			*op->resnull = fcinfo->argnull[0];
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_SQLVALUEFUNCTION)
  		{
  			/*
  			 * Doesn't seem worthwhile to have an inline implementation
  			 * efficiency-wise.
  			 */
! 			ExecEvalSQLValueFunction(state, op);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_CURRENTOFEXPR)
  		{
  			/* error invocation uses space, and shouldn't ever occur */
! 			ExecEvalCurrentOfExpr(state, op);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_NEXTVALUEEXPR)
  		{
  			/*
  			 * Doesn't seem worthwhile to have an inline implementation
  			 * efficiency-wise.
  			 */
! 			ExecEvalNextValueExpr(state, op);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_ARRAYEXPR)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalArrayExpr(state, op);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_ARRAYCOERCE)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalArrayCoerce(state, op);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_ROW)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalRow(state, op);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_ROWCOMPARE_STEP)
  		{
! 			FunctionCallInfo fcinfo = op->d.rowcompare_step.fcinfo_data;
  
  			/* force NULL result if strict fn and NULL input */
! 			if (op->d.rowcompare_step.finfo->fn_strict &&
  				(fcinfo->argnull[0] || fcinfo->argnull[1]))
  			{
! 				*op->resnull = true;
! 				EEO_JUMP(op->d.rowcompare_step.jumpnull);
  			}
  
  			/* Apply comparison function */
  			fcinfo->isnull = false;
! 			*op->resvalue = (op->d.rowcompare_step.fn_addr) (fcinfo);
  
  			/* force NULL result if NULL function result */
  			if (fcinfo->isnull)
  			{
! 				*op->resnull = true;
! 				EEO_JUMP(op->d.rowcompare_step.jumpnull);
  			}
! 			*op->resnull = false;
  
  			/* If unequal, no need to compare remaining columns */
! 			if (DatumGetInt32(*op->resvalue) != 0)
  			{
! 				EEO_JUMP(op->d.rowcompare_step.jumpdone);
  			}
  
  			EEO_NEXT();
--- 1283,1402 ----
  				Datum		result;
  
  				fcinfo->isnull = false;
! 				result = (sop->fn_addr) (fcinfo);
  
  				/* if the arguments are equal return null */
  				if (!fcinfo->isnull && DatumGetBool(result))
  				{
! 					*sop->resvalue = (Datum) 0;
! 					*sop->resnull = true;
  
  					EEO_NEXT();
  				}
  			}
  
  			/* Arguments aren't equal, so return the first one */
! 			*sop->resvalue = fcinfo->arg[0];
! 			*sop->resnull = fcinfo->argnull[0];
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_SQLVALUEFUNCTION)
  		{
+ 			DEF_SOP(ExprEvalStep_sqlvaluefunction);
+ 
  			/*
  			 * Doesn't seem worthwhile to have an inline implementation
  			 * efficiency-wise.
  			 */
! 			ExecEvalSQLValueFunction(state, sop);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_CURRENTOFEXPR)
  		{
+ 			DEF_SOP(ExprEvalStep);
+ 
  			/* error invocation uses space, and shouldn't ever occur */
! 			ExecEvalCurrentOfExpr(state, sop);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_NEXTVALUEEXPR)
  		{
+ 			DEF_SOP(ExprEvalStep_nextvalueexpr);
+ 
  			/*
  			 * Doesn't seem worthwhile to have an inline implementation
  			 * efficiency-wise.
  			 */
! 			ExecEvalNextValueExpr(state, sop);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_ARRAYEXPR)
  		{
+ 			DEF_SOP(ExprEvalStep_arrayexpr);
+ 
  			/* too complex for an inline implementation */
! 			ExecEvalArrayExpr(state, sop);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_ARRAYCOERCE)
  		{
+ 			DEF_SOP(ExprEvalStep_arraycoerce);
+ 
  			/* too complex for an inline implementation */
! 			ExecEvalArrayCoerce(state, sop);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_ROW)
  		{
+ 			DEF_SOP(ExprEvalStep_row);
+ 
  			/* too complex for an inline implementation */
! 			ExecEvalRow(state, sop);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_ROWCOMPARE_STEP)
  		{
! 			DEF_SOP(ExprEvalStep_rowcompare_step);
! 			FunctionCallInfo fcinfo = sop->fcinfo_data;
  
  			/* force NULL result if strict fn and NULL input */
! 			if (sop->finfo->fn_strict &&
  				(fcinfo->argnull[0] || fcinfo->argnull[1]))
  			{
! 				*sop->resnull = true;
! 				EEO_JUMP(sop->jumpnull);
  			}
  
  			/* Apply comparison function */
  			fcinfo->isnull = false;
! 			*sop->resvalue = (sop->fn_addr) (fcinfo);
  
  			/* force NULL result if NULL function result */
  			if (fcinfo->isnull)
  			{
! 				*sop->resnull = true;
! 				EEO_JUMP(sop->jumpnull);
  			}
! 			*sop->resnull = false;
  
  			/* If unequal, no need to compare remaining columns */
! 			if (DatumGetInt32(*sop->resvalue) != 0)
  			{
! 				EEO_JUMP(sop->jumpdone);
  			}
  
  			EEO_NEXT();
***************
*** 1300,1323 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_ROWCOMPARE_FINAL)
  		{
! 			int32		cmpresult = DatumGetInt32(*op->resvalue);
! 			RowCompareType rctype = op->d.rowcompare_final.rctype;
  
! 			*op->resnull = false;
  			switch (rctype)
  			{
  					/* EQ and NE cases aren't allowed here */
  				case ROWCOMPARE_LT:
! 					*op->resvalue = BoolGetDatum(cmpresult < 0);
  					break;
  				case ROWCOMPARE_LE:
! 					*op->resvalue = BoolGetDatum(cmpresult <= 0);
  					break;
  				case ROWCOMPARE_GE:
! 					*op->resvalue = BoolGetDatum(cmpresult >= 0);
  					break;
  				case ROWCOMPARE_GT:
! 					*op->resvalue = BoolGetDatum(cmpresult > 0);
  					break;
  				default:
  					Assert(false);
--- 1404,1428 ----
  
  		EEO_CASE(EEOP_ROWCOMPARE_FINAL)
  		{
! 			DEF_SOP(ExprEvalStep_rowcompare_final);
! 			int32		cmpresult = DatumGetInt32(*sop->resvalue);
! 			RowCompareType rctype = sop->rctype;
  
! 			*sop->resnull = false;
  			switch (rctype)
  			{
  					/* EQ and NE cases aren't allowed here */
  				case ROWCOMPARE_LT:
! 					*sop->resvalue = BoolGetDatum(cmpresult < 0);
  					break;
  				case ROWCOMPARE_LE:
! 					*sop->resvalue = BoolGetDatum(cmpresult <= 0);
  					break;
  				case ROWCOMPARE_GE:
! 					*sop->resvalue = BoolGetDatum(cmpresult >= 0);
  					break;
  				case ROWCOMPARE_GT:
! 					*sop->resvalue = BoolGetDatum(cmpresult > 0);
  					break;
  				default:
  					Assert(false);
***************
*** 1329,1382 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  
  		EEO_CASE(EEOP_MINMAX)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalMinMax(state, op);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_FIELDSELECT)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalFieldSelect(state, op, econtext);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_FIELDSTORE_DEFORM)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalFieldStoreDeForm(state, op, econtext);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_FIELDSTORE_FORM)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalFieldStoreForm(state, op, econtext);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_ARRAYREF_SUBSCRIPT)
  		{
  			/* Process an array subscript */
  
  			/* too complex for an inline implementation */
! 			if (ExecEvalArrayRefSubscript(state, op))
  			{
  				EEO_NEXT();
  			}
  			else
  			{
  				/* Subscript is null, short-circuit ArrayRef to NULL */
! 				EEO_JUMP(op->d.arrayref_subscript.jumpdone);
  			}
  		}
  
  		EEO_CASE(EEOP_ARRAYREF_OLD)
  		{
  			/*
  			 * Fetch the old value in an arrayref assignment, in case it's
  			 * referenced (via a CaseTestExpr) inside the assignment
--- 1434,1499 ----
  
  		EEO_CASE(EEOP_MINMAX)
  		{
+ 			DEF_SOP(ExprEvalStep_minmax);
+ 
  			/* too complex for an inline implementation */
! 			ExecEvalMinMax(state, sop);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_FIELDSELECT)
  		{
+ 			DEF_SOP(ExprEvalStep_fieldselect);
+ 
  			/* too complex for an inline implementation */
! 			ExecEvalFieldSelect(state, sop, econtext);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_FIELDSTORE_DEFORM)
  		{
+ 			DEF_SOP(ExprEvalStep_fieldstore);
+ 
  			/* too complex for an inline implementation */
! 			ExecEvalFieldStoreDeForm(state, sop, econtext);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_FIELDSTORE_FORM)
  		{
+ 			DEF_SOP(ExprEvalStep_fieldstore);
+ 
  			/* too complex for an inline implementation */
! 			ExecEvalFieldStoreForm(state, sop, econtext);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_ARRAYREF_SUBSCRIPT)
  		{
+ 			DEF_SOP(ExprEvalStep_arrayref_subscript);
+ 
  			/* Process an array subscript */
  
  			/* too complex for an inline implementation */
! 			if (ExecEvalArrayRefSubscript(state, sop))
  			{
  				EEO_NEXT();
  			}
  			else
  			{
  				/* Subscript is null, short-circuit ArrayRef to NULL */
! 				EEO_JUMP(sop->jumpdone);
  			}
  		}
  
  		EEO_CASE(EEOP_ARRAYREF_OLD)
  		{
+ 			DEF_SOP(ExprEvalStep_arrayref);
+ 
  			/*
  			 * Fetch the old value in an arrayref assignment, in case it's
  			 * referenced (via a CaseTestExpr) inside the assignment
***************
*** 1384,1390 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  			 */
  
  			/* too complex for an inline implementation */
! 			ExecEvalArrayRefOld(state, op);
  
  			EEO_NEXT();
  		}
--- 1501,1507 ----
  			 */
  
  			/* too complex for an inline implementation */
! 			ExecEvalArrayRefOld(state, sop);
  
  			EEO_NEXT();
  		}
***************
*** 1394,1401 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  		 */
  		EEO_CASE(EEOP_ARRAYREF_ASSIGN)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalArrayRefAssign(state, op);
  
  			EEO_NEXT();
  		}
--- 1511,1520 ----
  		 */
  		EEO_CASE(EEOP_ARRAYREF_ASSIGN)
  		{
+ 			DEF_SOP(ExprEvalStep_arrayref);
+ 
  			/* too complex for an inline implementation */
! 			ExecEvalArrayRefAssign(state, sop);
  
  			EEO_NEXT();
  		}
***************
*** 1405,1452 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  		 */
  		EEO_CASE(EEOP_ARRAYREF_FETCH)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalArrayRefFetch(state, op);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_CONVERT_ROWTYPE)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalConvertRowtype(state, op, econtext);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_SCALARARRAYOP)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalScalarArrayOp(state, op);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_DOMAIN_NOTNULL)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalConstraintNotNull(state, op);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_DOMAIN_CHECK)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalConstraintCheck(state, op);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_XMLEXPR)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalXmlExpr(state, op);
  
  			EEO_NEXT();
  		}
--- 1524,1583 ----
  		 */
  		EEO_CASE(EEOP_ARRAYREF_FETCH)
  		{
+ 			DEF_SOP(ExprEvalStep_arrayref);
+ 
  			/* too complex for an inline implementation */
! 			ExecEvalArrayRefFetch(state, sop);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_CONVERT_ROWTYPE)
  		{
+ 			DEF_SOP(ExprEvalStep_convert_rowtype);
+ 
  			/* too complex for an inline implementation */
! 			ExecEvalConvertRowtype(state, sop, econtext);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_SCALARARRAYOP)
  		{
+ 			DEF_SOP(ExprEvalStep_scalararrayop);
+ 
  			/* too complex for an inline implementation */
! 			ExecEvalScalarArrayOp(state, sop);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_DOMAIN_NOTNULL)
  		{
+ 			DEF_SOP(ExprEvalStep_domaincheck);
+ 
  			/* too complex for an inline implementation */
! 			ExecEvalConstraintNotNull(state, sop);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_DOMAIN_CHECK)
  		{
+ 			DEF_SOP(ExprEvalStep_domaincheck);
+ 
  			/* too complex for an inline implementation */
! 			ExecEvalConstraintCheck(state, sop);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_XMLEXPR)
  		{
+ 			DEF_SOP(ExprEvalStep_xmlexpr);
+ 
  			/* too complex for an inline implementation */
! 			ExecEvalXmlExpr(state, sop);
  
  			EEO_NEXT();
  		}
***************
*** 1457,1476 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  			 * Returns a Datum whose value is the precomputed aggregate value
  			 * found in the given expression context.
  			 */
! 			AggrefExprState *aggref = op->d.aggref.astate;
  
  			Assert(econtext->ecxt_aggvalues != NULL);
  
! 			*op->resvalue = econtext->ecxt_aggvalues[aggref->aggno];
! 			*op->resnull = econtext->ecxt_aggnulls[aggref->aggno];
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_GROUPING_FUNC)
  		{
  			/* too complex/uncommon for an inline implementation */
! 			ExecEvalGroupingFunc(state, op);
  
  			EEO_NEXT();
  		}
--- 1588,1610 ----
  			 * Returns a Datum whose value is the precomputed aggregate value
  			 * found in the given expression context.
  			 */
! 			DEF_SOP(ExprEvalStep_aggref);
! 			AggrefExprState *aggref = sop->astate;
  
  			Assert(econtext->ecxt_aggvalues != NULL);
  
! 			*sop->resvalue = econtext->ecxt_aggvalues[aggref->aggno];
! 			*sop->resnull = econtext->ecxt_aggnulls[aggref->aggno];
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_GROUPING_FUNC)
  		{
+ 			DEF_SOP(ExprEvalStep_grouping_func);
+ 
  			/* too complex/uncommon for an inline implementation */
! 			ExecEvalGroupingFunc(state, sop);
  
  			EEO_NEXT();
  		}
***************
*** 1480,1507 **** ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
  			/*
  			 * Like Aggref, just return a precomputed value from the econtext.
  			 */
! 			WindowFuncExprState *wfunc = op->d.window_func.wfstate;
  
  			Assert(econtext->ecxt_aggvalues != NULL);
  
! 			*op->resvalue = econtext->ecxt_aggvalues[wfunc->wfuncno];
! 			*op->resnull = econtext->ecxt_aggnulls[wfunc->wfuncno];
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_SUBPLAN)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalSubPlan(state, op, econtext);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_ALTERNATIVE_SUBPLAN)
  		{
  			/* too complex for an inline implementation */
! 			ExecEvalAlternativeSubPlan(state, op, econtext);
  
  			EEO_NEXT();
  		}
--- 1614,1646 ----
  			/*
  			 * Like Aggref, just return a precomputed value from the econtext.
  			 */
! 			DEF_SOP(ExprEvalStep_window_func);
! 			WindowFuncExprState *wfunc = sop->wfstate;
  
  			Assert(econtext->ecxt_aggvalues != NULL);
  
! 			*sop->resvalue = econtext->ecxt_aggvalues[wfunc->wfuncno];
! 			*sop->resnull = econtext->ecxt_aggnulls[wfunc->wfuncno];
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_SUBPLAN)
  		{
+ 			DEF_SOP(ExprEvalStep_subplan);
+ 			
  			/* too complex for an inline implementation */
! 			ExecEvalSubPlan(state, sop, econtext);
  
  			EEO_NEXT();
  		}
  
  		EEO_CASE(EEOP_ALTERNATIVE_SUBPLAN)
  		{
+ 			DEF_SOP(ExprEvalStep_alternative_subplan);
+ 			
  			/* too complex for an inline implementation */
! 			ExecEvalAlternativeSubPlan(state, sop, econtext);
  
  			EEO_NEXT();
  		}
***************
*** 1635,1648 **** ShutdownTupleDescRef(Datum arg)
  static Datum
  ExecJustInnerVarFirst(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = &state->steps[1];
! 	int			attnum = op->d.var.attnum + 1;
  	TupleTableSlot *slot = econtext->ecxt_innertuple;
  
  	/* See ExecInterpExpr()'s comments for EEOP_INNER_VAR_FIRST */
  
! 	CheckVarSlotCompatibility(slot, attnum, op->d.var.vartype);
! 	op->opcode = EEOP_INNER_VAR;	/* just for cleanliness */
  	state->evalfunc = ExecJustInnerVar;
  
  	/*
--- 1774,1788 ----
  static Datum
  ExecJustInnerVarFirst(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = state->steps->nextstep;
! 	DEF_SOP(ExprEvalStep_var);
! 	int			attnum = sop->attnum + 1;
  	TupleTableSlot *slot = econtext->ecxt_innertuple;
  
  	/* See ExecInterpExpr()'s comments for EEOP_INNER_VAR_FIRST */
  
! 	CheckVarSlotCompatibility(slot, attnum, sop->vartype);
! 	sop->opcode = EEOP_INNER_VAR;	/* just for cleanliness */
  	state->evalfunc = ExecJustInnerVar;
  
  	/*
***************
*** 1657,1664 **** ExecJustInnerVarFirst(ExprState *state, ExprContext *econtext, bool *isnull)
  static Datum
  ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = &state->steps[1];
! 	int			attnum = op->d.var.attnum + 1;
  	TupleTableSlot *slot = econtext->ecxt_innertuple;
  
  	/* See comments in ExecJustInnerVarFirst */
--- 1797,1805 ----
  static Datum
  ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = state->steps->nextstep;
! 	DEF_SOP(ExprEvalStep_var);
! 	int			attnum = sop->attnum + 1;
  	TupleTableSlot *slot = econtext->ecxt_innertuple;
  
  	/* See comments in ExecJustInnerVarFirst */
***************
*** 1669,1680 **** ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
  static Datum
  ExecJustOuterVarFirst(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = &state->steps[1];
! 	int			attnum = op->d.var.attnum + 1;
  	TupleTableSlot *slot = econtext->ecxt_outertuple;
  
! 	CheckVarSlotCompatibility(slot, attnum, op->d.var.vartype);
! 	op->opcode = EEOP_OUTER_VAR;	/* just for cleanliness */
  	state->evalfunc = ExecJustOuterVar;
  
  	/* See comments in ExecJustInnerVarFirst */
--- 1810,1822 ----
  static Datum
  ExecJustOuterVarFirst(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = state->steps->nextstep;
! 	DEF_SOP(ExprEvalStep_var);
! 	int			attnum = sop->attnum + 1;
  	TupleTableSlot *slot = econtext->ecxt_outertuple;
  
! 	CheckVarSlotCompatibility(slot, attnum, sop->vartype);
! 	sop->opcode = EEOP_OUTER_VAR;	/* just for cleanliness */
  	state->evalfunc = ExecJustOuterVar;
  
  	/* See comments in ExecJustInnerVarFirst */
***************
*** 1685,1692 **** ExecJustOuterVarFirst(ExprState *state, ExprContext *econtext, bool *isnull)
  static Datum
  ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = &state->steps[1];
! 	int			attnum = op->d.var.attnum + 1;
  	TupleTableSlot *slot = econtext->ecxt_outertuple;
  
  	/* See comments in ExecJustInnerVarFirst */
--- 1827,1835 ----
  static Datum
  ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = state->steps->nextstep;
! 	DEF_SOP(ExprEvalStep_var);
! 	int			attnum = sop->attnum + 1;
  	TupleTableSlot *slot = econtext->ecxt_outertuple;
  
  	/* See comments in ExecJustInnerVarFirst */
***************
*** 1697,1708 **** ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
  static Datum
  ExecJustScanVarFirst(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = &state->steps[1];
! 	int			attnum = op->d.var.attnum + 1;
  	TupleTableSlot *slot = econtext->ecxt_scantuple;
  
! 	CheckVarSlotCompatibility(slot, attnum, op->d.var.vartype);
! 	op->opcode = EEOP_SCAN_VAR; /* just for cleanliness */
  	state->evalfunc = ExecJustScanVar;
  
  	/* See comments in ExecJustInnerVarFirst */
--- 1840,1852 ----
  static Datum
  ExecJustScanVarFirst(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = state->steps->nextstep;
! 	DEF_SOP(ExprEvalStep_var);
! 	int			attnum = sop->attnum + 1;
  	TupleTableSlot *slot = econtext->ecxt_scantuple;
  
! 	CheckVarSlotCompatibility(slot, attnum, sop->vartype);
! 	sop->opcode = EEOP_SCAN_VAR; /* just for cleanliness */
  	state->evalfunc = ExecJustScanVar;
  
  	/* See comments in ExecJustInnerVarFirst */
***************
*** 1713,1720 **** ExecJustScanVarFirst(ExprState *state, ExprContext *econtext, bool *isnull)
  static Datum
  ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = &state->steps[1];
! 	int			attnum = op->d.var.attnum + 1;
  	TupleTableSlot *slot = econtext->ecxt_scantuple;
  
  	/* See comments in ExecJustInnerVarFirst */
--- 1857,1865 ----
  static Datum
  ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = state->steps->nextstep;
! 	DEF_SOP(ExprEvalStep_var);
! 	int			attnum = sop->attnum + 1;
  	TupleTableSlot *slot = econtext->ecxt_scantuple;
  
  	/* See comments in ExecJustInnerVarFirst */
***************
*** 1725,1743 **** ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
  static Datum
  ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = &state->steps[0];
  
! 	*isnull = op->d.constval.isnull;
! 	return op->d.constval.value;
  }
  
  /* Evaluate inner Var and assign to appropriate column of result tuple */
  static Datum
  ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = &state->steps[1];
! 	int			attnum = op->d.assign_var.attnum + 1;
! 	int			resultnum = op->d.assign_var.resultnum;
  	TupleTableSlot *inslot = econtext->ecxt_innertuple;
  	TupleTableSlot *outslot = state->resultslot;
  
--- 1870,1890 ----
  static Datum
  ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = state->steps;
! 	DEF_SOP(ExprEvalStep_constval);
  
! 	*isnull = sop->isnull;
! 	return sop->value;
  }
  
  /* Evaluate inner Var and assign to appropriate column of result tuple */
  static Datum
  ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = state->steps->nextstep;
! 	DEF_SOP(ExprEvalStep_assign_var);
! 	int			attnum = sop->attnum + 1;
! 	int			resultnum = sop->resultnum;
  	TupleTableSlot *inslot = econtext->ecxt_innertuple;
  	TupleTableSlot *outslot = state->resultslot;
  
***************
*** 1758,1766 **** ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
  static Datum
  ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = &state->steps[1];
! 	int			attnum = op->d.assign_var.attnum + 1;
! 	int			resultnum = op->d.assign_var.resultnum;
  	TupleTableSlot *inslot = econtext->ecxt_outertuple;
  	TupleTableSlot *outslot = state->resultslot;
  
--- 1905,1914 ----
  static Datum
  ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = state->steps->nextstep;
! 	DEF_SOP(ExprEvalStep_assign_var);
! 	int			attnum = sop->attnum + 1;
! 	int			resultnum = sop->resultnum;
  	TupleTableSlot *inslot = econtext->ecxt_outertuple;
  	TupleTableSlot *outslot = state->resultslot;
  
***************
*** 1774,1782 **** ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
  static Datum
  ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = &state->steps[1];
! 	int			attnum = op->d.assign_var.attnum + 1;
! 	int			resultnum = op->d.assign_var.resultnum;
  	TupleTableSlot *inslot = econtext->ecxt_scantuple;
  	TupleTableSlot *outslot = state->resultslot;
  
--- 1922,1931 ----
  static Datum
  ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
  {
! 	ExprEvalStep *op = state->steps->nextstep;
! 	DEF_SOP(ExprEvalStep_assign_var);
! 	int			attnum = sop->attnum + 1;
! 	int			resultnum = sop->resultnum;
  	TupleTableSlot *inslot = econtext->ecxt_scantuple;
  	TupleTableSlot *outslot = state->resultslot;
  
***************
*** 1844,1854 **** ExecEvalStepOp(ExprState *state, ExprEvalStep *op)
   * ecxt_param_exec_vals array, and can be accessed by array index.
   */
  void
! ExecEvalParamExec(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  {
  	ParamExecData *prm;
  
! 	prm = &(econtext->ecxt_param_exec_vals[op->d.param.paramid]);
  	if (unlikely(prm->execPlan != NULL))
  	{
  		/* Parameter not evaluated yet, so go do it */
--- 1993,2004 ----
   * ecxt_param_exec_vals array, and can be accessed by array index.
   */
  void
! ExecEvalParamExec(ExprState *state, ExprEvalStep_param *sop,
!                   ExprContext *econtext)
  {
  	ParamExecData *prm;
  
! 	prm = &(econtext->ecxt_param_exec_vals[sop->paramid]);
  	if (unlikely(prm->execPlan != NULL))
  	{
  		/* Parameter not evaluated yet, so go do it */
***************
*** 1856,1863 **** ExecEvalParamExec(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  		/* ExecSetParamPlan should have processed this param... */
  		Assert(prm->execPlan == NULL);
  	}
! 	*op->resvalue = prm->value;
! 	*op->resnull = prm->isnull;
  }
  
  /*
--- 2006,2013 ----
  		/* ExecSetParamPlan should have processed this param... */
  		Assert(prm->execPlan == NULL);
  	}
! 	*sop->resvalue = prm->value;
! 	*sop->resnull = prm->isnull;
  }
  
  /*
***************
*** 1866,1875 **** ExecEvalParamExec(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
   * PARAM_EXTERN parameters must be sought in ecxt_param_list_info.
   */
  void
! ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  {
  	ParamListInfo paramInfo = econtext->ecxt_param_list_info;
! 	int			paramId = op->d.param.paramid;
  
  	if (likely(paramInfo &&
  			   paramId > 0 && paramId <= paramInfo->numParams))
--- 2016,2026 ----
   * PARAM_EXTERN parameters must be sought in ecxt_param_list_info.
   */
  void
! ExecEvalParamExtern(ExprState *state, ExprEvalStep_param *sop,
!                     ExprContext *econtext)
  {
  	ParamListInfo paramInfo = econtext->ecxt_param_list_info;
! 	int			paramId = sop->paramid;
  
  	if (likely(paramInfo &&
  			   paramId > 0 && paramId <= paramInfo->numParams))
***************
*** 1883,1897 **** ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  		if (likely(OidIsValid(prm->ptype)))
  		{
  			/* safety check in case hook did something unexpected */
! 			if (unlikely(prm->ptype != op->d.param.paramtype))
  				ereport(ERROR,
  						(errcode(ERRCODE_DATATYPE_MISMATCH),
  						 errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
  								paramId,
  								format_type_be(prm->ptype),
! 								format_type_be(op->d.param.paramtype))));
! 			*op->resvalue = prm->value;
! 			*op->resnull = prm->isnull;
  			return;
  		}
  	}
--- 2034,2048 ----
  		if (likely(OidIsValid(prm->ptype)))
  		{
  			/* safety check in case hook did something unexpected */
! 			if (unlikely(prm->ptype != sop->paramtype))
  				ereport(ERROR,
  						(errcode(ERRCODE_DATATYPE_MISMATCH),
  						 errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
  								paramId,
  								format_type_be(prm->ptype),
! 								format_type_be(sop->paramtype))));
! 			*sop->resvalue = prm->value;
! 			*sop->resnull = prm->isnull;
  			return;
  		}
  	}
***************
*** 1905,1916 **** ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
   * Evaluate a SQLValueFunction expression.
   */
  void
! ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
  {
! 	SQLValueFunction *svf = op->d.sqlvaluefunction.svf;
  	FunctionCallInfoData fcinfo;
  
! 	*op->resnull = false;
  
  	/*
  	 * Note: current_schema() can return NULL.  current_user() etc currently
--- 2056,2067 ----
   * Evaluate a SQLValueFunction expression.
   */
  void
! ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep_sqlvaluefunction *sop)
  {
! 	SQLValueFunction *svf = sop->svf;
  	FunctionCallInfoData fcinfo;
  
! 	*sop->resnull = false;
  
  	/*
  	 * Note: current_schema() can return NULL.  current_user() etc currently
***************
*** 1919,1963 **** ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
  	switch (svf->op)
  	{
  		case SVFOP_CURRENT_DATE:
! 			*op->resvalue = DateADTGetDatum(GetSQLCurrentDate());
  			break;
  		case SVFOP_CURRENT_TIME:
  		case SVFOP_CURRENT_TIME_N:
! 			*op->resvalue = TimeTzADTPGetDatum(GetSQLCurrentTime(svf->typmod));
  			break;
  		case SVFOP_CURRENT_TIMESTAMP:
  		case SVFOP_CURRENT_TIMESTAMP_N:
! 			*op->resvalue = TimestampTzGetDatum(GetSQLCurrentTimestamp(svf->typmod));
  			break;
  		case SVFOP_LOCALTIME:
  		case SVFOP_LOCALTIME_N:
! 			*op->resvalue = TimeADTGetDatum(GetSQLLocalTime(svf->typmod));
  			break;
  		case SVFOP_LOCALTIMESTAMP:
  		case SVFOP_LOCALTIMESTAMP_N:
! 			*op->resvalue = TimestampGetDatum(GetSQLLocalTimestamp(svf->typmod));
  			break;
  		case SVFOP_CURRENT_ROLE:
  		case SVFOP_CURRENT_USER:
  		case SVFOP_USER:
  			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
! 			*op->resvalue = current_user(&fcinfo);
! 			*op->resnull = fcinfo.isnull;
  			break;
  		case SVFOP_SESSION_USER:
  			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
! 			*op->resvalue = session_user(&fcinfo);
! 			*op->resnull = fcinfo.isnull;
  			break;
  		case SVFOP_CURRENT_CATALOG:
  			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
! 			*op->resvalue = current_database(&fcinfo);
! 			*op->resnull = fcinfo.isnull;
  			break;
  		case SVFOP_CURRENT_SCHEMA:
  			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
! 			*op->resvalue = current_schema(&fcinfo);
! 			*op->resnull = fcinfo.isnull;
  			break;
  	}
  }
--- 2070,2114 ----
  	switch (svf->op)
  	{
  		case SVFOP_CURRENT_DATE:
! 			*sop->resvalue = DateADTGetDatum(GetSQLCurrentDate());
  			break;
  		case SVFOP_CURRENT_TIME:
  		case SVFOP_CURRENT_TIME_N:
! 			*sop->resvalue = TimeTzADTPGetDatum(GetSQLCurrentTime(svf->typmod));
  			break;
  		case SVFOP_CURRENT_TIMESTAMP:
  		case SVFOP_CURRENT_TIMESTAMP_N:
! 			*sop->resvalue = TimestampTzGetDatum(GetSQLCurrentTimestamp(svf->typmod));
  			break;
  		case SVFOP_LOCALTIME:
  		case SVFOP_LOCALTIME_N:
! 			*sop->resvalue = TimeADTGetDatum(GetSQLLocalTime(svf->typmod));
  			break;
  		case SVFOP_LOCALTIMESTAMP:
  		case SVFOP_LOCALTIMESTAMP_N:
! 			*sop->resvalue = TimestampGetDatum(GetSQLLocalTimestamp(svf->typmod));
  			break;
  		case SVFOP_CURRENT_ROLE:
  		case SVFOP_CURRENT_USER:
  		case SVFOP_USER:
  			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
! 			*sop->resvalue = current_user(&fcinfo);
! 			*sop->resnull = fcinfo.isnull;
  			break;
  		case SVFOP_SESSION_USER:
  			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
! 			*sop->resvalue = session_user(&fcinfo);
! 			*sop->resnull = fcinfo.isnull;
  			break;
  		case SVFOP_CURRENT_CATALOG:
  			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
! 			*sop->resvalue = current_database(&fcinfo);
! 			*sop->resnull = fcinfo.isnull;
  			break;
  		case SVFOP_CURRENT_SCHEMA:
  			InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
! 			*sop->resvalue = current_schema(&fcinfo);
! 			*sop->resnull = fcinfo.isnull;
  			break;
  	}
  }
***************
*** 1972,1978 **** ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
   * table whose FDW doesn't handle it, and complain accordingly.
   */
  void
! ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op)
  {
  	ereport(ERROR,
  			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
--- 2123,2129 ----
   * table whose FDW doesn't handle it, and complain accordingly.
   */
  void
! ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *sop)
  {
  	ereport(ERROR,
  			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
***************
*** 1983,2035 **** ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op)
   * Evaluate NextValueExpr.
   */
  void
! ExecEvalNextValueExpr(ExprState *state, ExprEvalStep *op)
  {
! 	int64		newval = nextval_internal(op->d.nextvalueexpr.seqid, false);
  
! 	switch (op->d.nextvalueexpr.seqtypid)
  	{
  		case INT2OID:
! 			*op->resvalue = Int16GetDatum((int16) newval);
  			break;
  		case INT4OID:
! 			*op->resvalue = Int32GetDatum((int32) newval);
  			break;
  		case INT8OID:
! 			*op->resvalue = Int64GetDatum((int64) newval);
  			break;
  		default:
  			elog(ERROR, "unsupported sequence type %u",
! 				 op->d.nextvalueexpr.seqtypid);
  	}
! 	*op->resnull = false;
  }
  
  /*
   * Evaluate NullTest / IS NULL for rows.
   */
  void
! ExecEvalRowNull(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  {
! 	ExecEvalRowNullInt(state, op, econtext, true);
  }
  
  /*
   * Evaluate NullTest / IS NOT NULL for rows.
   */
  void
! ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  {
! 	ExecEvalRowNullInt(state, op, econtext, false);
  }
  
  /* Common code for IS [NOT] NULL on a row value */
  static void
! ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
  				   ExprContext *econtext, bool checkisnull)
  {
! 	Datum		value = *op->resvalue;
! 	bool		isnull = *op->resnull;
  	HeapTupleHeader tuple;
  	Oid			tupType;
  	int32		tupTypmod;
--- 2134,2188 ----
   * Evaluate NextValueExpr.
   */
  void
! ExecEvalNextValueExpr(ExprState *state, ExprEvalStep_nextvalueexpr *sop)
  {
! 	int64		newval = nextval_internal(sop->seqid, false);
  
! 	switch (sop->seqtypid)
  	{
  		case INT2OID:
! 			*sop->resvalue = Int16GetDatum((int16) newval);
  			break;
  		case INT4OID:
! 			*sop->resvalue = Int32GetDatum((int32) newval);
  			break;
  		case INT8OID:
! 			*sop->resvalue = Int64GetDatum((int64) newval);
  			break;
  		default:
  			elog(ERROR, "unsupported sequence type %u",
! 				 sop->seqtypid);
  	}
! 	*sop->resnull = false;
  }
  
  /*
   * Evaluate NullTest / IS NULL for rows.
   */
  void
! ExecEvalRowNull(ExprState *state, ExprEvalStep_nulltest_row *sop,
!                 ExprContext *econtext)
  {
! 	ExecEvalRowNullInt(state, sop, econtext, true);
  }
  
  /*
   * Evaluate NullTest / IS NOT NULL for rows.
   */
  void
! ExecEvalRowNotNull(ExprState *state, ExprEvalStep_nulltest_row *sop,
!                    ExprContext *econtext)
  {
! 	ExecEvalRowNullInt(state, sop, econtext, false);
  }
  
  /* Common code for IS [NOT] NULL on a row value */
  static void
! ExecEvalRowNullInt(ExprState *state, ExprEvalStep_nulltest_row *sop,
  				   ExprContext *econtext, bool checkisnull)
  {
! 	Datum		value = *sop->resvalue;
! 	bool		isnull = *sop->resnull;
  	HeapTupleHeader tuple;
  	Oid			tupType;
  	int32		tupTypmod;
***************
*** 2037,2048 **** ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
  	HeapTupleData tmptup;
  	int			att;
  
! 	*op->resnull = false;
  
  	/* NULL row variables are treated just as NULL scalar columns */
  	if (isnull)
  	{
! 		*op->resvalue = BoolGetDatum(checkisnull);
  		return;
  	}
  
--- 2190,2201 ----
  	HeapTupleData tmptup;
  	int			att;
  
! 	*sop->resnull = false;
  
  	/* NULL row variables are treated just as NULL scalar columns */
  	if (isnull)
  	{
! 		*sop->resvalue = BoolGetDatum(checkisnull);
  		return;
  	}
  
***************
*** 2069,2075 **** ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
  
  	/* Lookup tupdesc if first time through or if type changes */
  	tupDesc = get_cached_rowtype(tupType, tupTypmod,
! 								 &op->d.nulltest_row.argdesc,
  								 econtext);
  
  	/*
--- 2222,2228 ----
  
  	/* Lookup tupdesc if first time through or if type changes */
  	tupDesc = get_cached_rowtype(tupType, tupTypmod,
! 								 &sop->argdesc,
  								 econtext);
  
  	/*
***************
*** 2088,2094 **** ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
  			/* null field disproves IS NOT NULL */
  			if (!checkisnull)
  			{
! 				*op->resvalue = BoolGetDatum(false);
  				return;
  			}
  		}
--- 2241,2247 ----
  			/* null field disproves IS NOT NULL */
  			if (!checkisnull)
  			{
! 				*sop->resvalue = BoolGetDatum(false);
  				return;
  			}
  		}
***************
*** 2097,2140 **** ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
  			/* non-null field disproves IS NULL */
  			if (checkisnull)
  			{
! 				*op->resvalue = BoolGetDatum(false);
  				return;
  			}
  		}
  	}
  
! 	*op->resvalue = BoolGetDatum(true);
  }
  
  /*
   * Evaluate an ARRAY[] expression.
   *
   * The individual array elements (or subarrays) have already been evaluated
!  * into op->d.arrayexpr.elemvalues[]/elemnulls[].
   */
  void
! ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op)
  {
  	ArrayType  *result;
! 	Oid			element_type = op->d.arrayexpr.elemtype;
! 	int			nelems = op->d.arrayexpr.nelems;
  	int			ndims = 0;
  	int			dims[MAXDIM];
  	int			lbs[MAXDIM];
  
  	/* Set non-null as default */
! 	*op->resnull = false;
  
! 	if (!op->d.arrayexpr.multidims)
  	{
  		/* Elements are presumably of scalar type */
! 		Datum	   *dvalues = op->d.arrayexpr.elemvalues;
! 		bool	   *dnulls = op->d.arrayexpr.elemnulls;
  
  		/* Shouldn't happen here, but if length is 0, return empty array */
  		if (nelems == 0)
  		{
! 			*op->resvalue =
  				PointerGetDatum(construct_empty_array(element_type));
  			return;
  		}
--- 2250,2293 ----
  			/* non-null field disproves IS NULL */
  			if (checkisnull)
  			{
! 				*sop->resvalue = BoolGetDatum(false);
  				return;
  			}
  		}
  	}
  
! 	*sop->resvalue = BoolGetDatum(true);
  }
  
  /*
   * Evaluate an ARRAY[] expression.
   *
   * The individual array elements (or subarrays) have already been evaluated
!  * into sop->elemvalues[]/elemnulls[].
   */
  void
! ExecEvalArrayExpr(ExprState *state, ExprEvalStep_arrayexpr *sop)
  {
  	ArrayType  *result;
! 	Oid			element_type = sop->elemtype;
! 	int			nelems = sop->nelems;
  	int			ndims = 0;
  	int			dims[MAXDIM];
  	int			lbs[MAXDIM];
  
  	/* Set non-null as default */
! 	*sop->resnull = false;
  
! 	if (!sop->multidims)
  	{
  		/* Elements are presumably of scalar type */
! 		Datum	   *dvalues = sop->elemvalues;
! 		bool	   *dnulls = sop->elemnulls;
  
  		/* Shouldn't happen here, but if length is 0, return empty array */
  		if (nelems == 0)
  		{
! 			*sop->resvalue =
  				PointerGetDatum(construct_empty_array(element_type));
  			return;
  		}
***************
*** 2146,2154 **** ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op)
  
  		result = construct_md_array(dvalues, dnulls, ndims, dims, lbs,
  									element_type,
! 									op->d.arrayexpr.elemlength,
! 									op->d.arrayexpr.elembyval,
! 									op->d.arrayexpr.elemalign);
  	}
  	else
  	{
--- 2299,2307 ----
  
  		result = construct_md_array(dvalues, dnulls, ndims, dims, lbs,
  									element_type,
! 									sop->elemlength,
! 									sop->elembyval,
! 									sop->elemalign);
  	}
  	else
  	{
***************
*** 2185,2192 **** ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op)
  			ArrayType  *array;
  			int			this_ndims;
  
! 			arraydatum = op->d.arrayexpr.elemvalues[elemoff];
! 			eisnull = op->d.arrayexpr.elemnulls[elemoff];
  
  			/* temporarily ignore null subarrays */
  			if (eisnull)
--- 2338,2345 ----
  			ArrayType  *array;
  			int			this_ndims;
  
! 			arraydatum = sop->elemvalues[elemoff];
! 			eisnull = sop->elemnulls[elemoff];
  
  			/* temporarily ignore null subarrays */
  			if (eisnull)
***************
*** 2268,2274 **** ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op)
  		{
  			if (ndims == 0)		/* didn't find any nonempty array */
  			{
! 				*op->resvalue = PointerGetDatum(construct_empty_array(element_type));
  				return;
  			}
  			ereport(ERROR,
--- 2421,2427 ----
  		{
  			if (ndims == 0)		/* didn't find any nonempty array */
  			{
! 				*sop->resvalue = PointerGetDatum(construct_empty_array(element_type));
  				return;
  			}
  			ereport(ERROR,
***************
*** 2319,2325 **** ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op)
  		}
  	}
  
! 	*op->resvalue = PointerGetDatum(result);
  }
  
  /*
--- 2472,2478 ----
  		}
  	}
  
! 	*sop->resvalue = PointerGetDatum(result);
  }
  
  /*
***************
*** 2328,2344 **** ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op)
   * Source array is in step's result variable.
   */
  void
! ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op)
  {
! 	ArrayCoerceExpr *acoerce = op->d.arraycoerce.coerceexpr;
  	Datum		arraydatum;
  	FunctionCallInfoData locfcinfo;
  
  	/* NULL array -> NULL result */
! 	if (*op->resnull)
  		return;
  
! 	arraydatum = *op->resvalue;
  
  	/*
  	 * If it's binary-compatible, modify the element type in the array header,
--- 2481,2497 ----
   * Source array is in step's result variable.
   */
  void
! ExecEvalArrayCoerce(ExprState *state, ExprEvalStep_arraycoerce *sop)
  {
! 	ArrayCoerceExpr *acoerce = sop->coerceexpr;
  	Datum		arraydatum;
  	FunctionCallInfoData locfcinfo;
  
  	/* NULL array -> NULL result */
! 	if (*sop->resnull)
  		return;
  
! 	arraydatum = *sop->resvalue;
  
  	/*
  	 * If it's binary-compatible, modify the element type in the array header,
***************
*** 2349,2356 **** ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op)
  		/* Detoast input array if necessary, and copy in any case */
  		ArrayType  *array = DatumGetArrayTypePCopy(arraydatum);
  
! 		ARR_ELEMTYPE(array) = op->d.arraycoerce.resultelemtype;
! 		*op->resvalue = PointerGetDatum(array);
  		return;
  	}
  
--- 2502,2509 ----
  		/* Detoast input array if necessary, and copy in any case */
  		ArrayType  *array = DatumGetArrayTypePCopy(arraydatum);
  
! 		ARR_ELEMTYPE(array) = sop->resultelemtype;
! 		*sop->resvalue = PointerGetDatum(array);
  		return;
  	}
  
***************
*** 2362,2368 **** ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op)
  	 *
  	 * Note: coercion functions are assumed to not use collation.
  	 */
! 	InitFunctionCallInfoData(locfcinfo, op->d.arraycoerce.elemfunc, 3,
  							 InvalidOid, NULL, NULL);
  	locfcinfo.arg[0] = arraydatum;
  	locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod);
--- 2515,2521 ----
  	 *
  	 * Note: coercion functions are assumed to not use collation.
  	 */
! 	InitFunctionCallInfoData(locfcinfo, sop->elemfunc, 3,
  							 InvalidOid, NULL, NULL);
  	locfcinfo.arg[0] = arraydatum;
  	locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod);
***************
*** 2371,2413 **** ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op)
  	locfcinfo.argnull[1] = false;
  	locfcinfo.argnull[2] = false;
  
! 	*op->resvalue = array_map(&locfcinfo, op->d.arraycoerce.resultelemtype,
! 							  op->d.arraycoerce.amstate);
  }
  
  /*
   * Evaluate a ROW() expression.
   *
   * The individual columns have already been evaluated into
!  * op->d.row.elemvalues[]/elemnulls[].
   */
  void
! ExecEvalRow(ExprState *state, ExprEvalStep *op)
  {
  	HeapTuple	tuple;
  
  	/* build tuple from evaluated field values */
! 	tuple = heap_form_tuple(op->d.row.tupdesc,
! 							op->d.row.elemvalues,
! 							op->d.row.elemnulls);
  
! 	*op->resvalue = HeapTupleGetDatum(tuple);
! 	*op->resnull = false;
  }
  
  /*
   * Evaluate GREATEST() or LEAST() expression (note this is *not* MIN()/MAX()).
   *
   * All of the to-be-compared expressions have already been evaluated into
!  * op->d.minmax.values[]/nulls[].
   */
  void
! ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
  {
! 	Datum	   *values = op->d.minmax.values;
! 	bool	   *nulls = op->d.minmax.nulls;
! 	FunctionCallInfo fcinfo = op->d.minmax.fcinfo_data;
! 	MinMaxOp	operator = op->d.minmax.op;
  	int			off;
  
  	/* set at initialization */
--- 2524,2565 ----
  	locfcinfo.argnull[1] = false;
  	locfcinfo.argnull[2] = false;
  
! 	*sop->resvalue = array_map(&locfcinfo, sop->resultelemtype, sop->amstate);
  }
  
  /*
   * Evaluate a ROW() expression.
   *
   * The individual columns have already been evaluated into
!  * sop->elemvalues[]/elemnulls[].
   */
  void
! ExecEvalRow(ExprState *state, ExprEvalStep_row *sop)
  {
  	HeapTuple	tuple;
  
  	/* build tuple from evaluated field values */
! 	tuple = heap_form_tuple(sop->tupdesc,
! 							sop->elemvalues,
! 							sop->elemnulls);
  
! 	*sop->resvalue = HeapTupleGetDatum(tuple);
! 	*sop->resnull = false;
  }
  
  /*
   * Evaluate GREATEST() or LEAST() expression (note this is *not* MIN()/MAX()).
   *
   * All of the to-be-compared expressions have already been evaluated into
!  * sop->values[]/nulls[].
   */
  void
! ExecEvalMinMax(ExprState *state, ExprEvalStep_minmax *sop)
  {
! 	Datum	   *values = sop->values;
! 	bool	   *nulls = sop->nulls;
! 	FunctionCallInfo fcinfo = sop->fcinfo_data;
! 	MinMaxOp	operator = sop->op;
  	int			off;
  
  	/* set at initialization */
***************
*** 2415,2440 **** ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
  	Assert(fcinfo->argnull[1] == false);
  
  	/* default to null result */
! 	*op->resnull = true;
  
! 	for (off = 0; off < op->d.minmax.nelems; off++)
  	{
  		/* ignore NULL inputs */
  		if (nulls[off])
  			continue;
  
! 		if (*op->resnull)
  		{
  			/* first nonnull input, adopt value */
! 			*op->resvalue = values[off];
! 			*op->resnull = false;
  		}
  		else
  		{
  			int			cmpresult;
  
  			/* apply comparison function */
! 			fcinfo->arg[0] = *op->resvalue;
  			fcinfo->arg[1] = values[off];
  
  			fcinfo->isnull = false;
--- 2567,2592 ----
  	Assert(fcinfo->argnull[1] == false);
  
  	/* default to null result */
! 	*sop->resnull = true;
  
! 	for (off = 0; off < sop->nelems; off++)
  	{
  		/* ignore NULL inputs */
  		if (nulls[off])
  			continue;
  
! 		if (*sop->resnull)
  		{
  			/* first nonnull input, adopt value */
! 			*sop->resvalue = values[off];
! 			*sop->resnull = false;
  		}
  		else
  		{
  			int			cmpresult;
  
  			/* apply comparison function */
! 			fcinfo->arg[0] = *sop->resvalue;
  			fcinfo->arg[1] = values[off];
  
  			fcinfo->isnull = false;
***************
*** 2443,2451 **** ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
  				continue;
  
  			if (cmpresult > 0 && operator == IS_LEAST)
! 				*op->resvalue = values[off];
  			else if (cmpresult < 0 && operator == IS_GREATEST)
! 				*op->resvalue = values[off];
  		}
  	}
  }
--- 2595,2603 ----
  				continue;
  
  			if (cmpresult > 0 && operator == IS_LEAST)
! 				*sop->resvalue = values[off];
  			else if (cmpresult < 0 && operator == IS_GREATEST)
! 				*sop->resvalue = values[off];
  		}
  	}
  }
***************
*** 2456,2464 **** ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
   * Source record is in step's result variable.
   */
  void
! ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  {
! 	AttrNumber	fieldnum = op->d.fieldselect.fieldnum;
  	Datum		tupDatum;
  	HeapTupleHeader tuple;
  	Oid			tupType;
--- 2608,2617 ----
   * Source record is in step's result variable.
   */
  void
! ExecEvalFieldSelect(ExprState *state, ExprEvalStep_fieldselect *sop,
!                     ExprContext *econtext)
  {
! 	AttrNumber	fieldnum = sop->fieldnum;
  	Datum		tupDatum;
  	HeapTupleHeader tuple;
  	Oid			tupType;
***************
*** 2468,2478 **** ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  	HeapTupleData tmptup;
  
  	/* NULL record -> NULL result */
! 	if (*op->resnull)
  		return;
  
  	/* Get the composite datum and extract its type fields */
! 	tupDatum = *op->resvalue;
  	tuple = DatumGetHeapTupleHeader(tupDatum);
  
  	tupType = HeapTupleHeaderGetTypeId(tuple);
--- 2621,2631 ----
  	HeapTupleData tmptup;
  
  	/* NULL record -> NULL result */
! 	if (*sop->resnull)
  		return;
  
  	/* Get the composite datum and extract its type fields */
! 	tupDatum = *sop->resvalue;
  	tuple = DatumGetHeapTupleHeader(tupDatum);
  
  	tupType = HeapTupleHeaderGetTypeId(tuple);
***************
*** 2480,2486 **** ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  
  	/* Lookup tupdesc if first time through or if type changes */
  	tupDesc = get_cached_rowtype(tupType, tupTypmod,
! 								 &op->d.fieldselect.argdesc,
  								 econtext);
  
  	/*
--- 2633,2639 ----
  
  	/* Lookup tupdesc if first time through or if type changes */
  	tupDesc = get_cached_rowtype(tupType, tupTypmod,
! 								 &sop->argdesc,
  								 econtext);
  
  	/*
***************
*** 2499,2527 **** ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  	/* Check for dropped column, and force a NULL result if so */
  	if (attr->attisdropped)
  	{
! 		*op->resnull = true;
  		return;
  	}
  
  	/* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
  	/* As in CheckVarSlotCompatibility, we should but can't check typmod */
! 	if (op->d.fieldselect.resulttype != attr->atttypid)
  		ereport(ERROR,
  				(errcode(ERRCODE_DATATYPE_MISMATCH),
  				 errmsg("attribute %d has wrong type", fieldnum),
  				 errdetail("Table has type %s, but query expects %s.",
  						   format_type_be(attr->atttypid),
! 						   format_type_be(op->d.fieldselect.resulttype))));
  
  	/* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
  	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
  	tmptup.t_data = tuple;
  
  	/* extract the field */
! 	*op->resvalue = heap_getattr(&tmptup,
  								 fieldnum,
  								 tupDesc,
! 								 op->resnull);
  }
  
  /*
--- 2652,2680 ----
  	/* Check for dropped column, and force a NULL result if so */
  	if (attr->attisdropped)
  	{
! 		*sop->resnull = true;
  		return;
  	}
  
  	/* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
  	/* As in CheckVarSlotCompatibility, we should but can't check typmod */
! 	if (sop->resulttype != attr->atttypid)
  		ereport(ERROR,
  				(errcode(ERRCODE_DATATYPE_MISMATCH),
  				 errmsg("attribute %d has wrong type", fieldnum),
  				 errdetail("Table has type %s, but query expects %s.",
  						   format_type_be(attr->atttypid),
! 						   format_type_be(sop->resulttype))));
  
  	/* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
  	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
  	tmptup.t_data = tuple;
  
  	/* extract the field */
! 	*sop->resvalue = heap_getattr(&tmptup,
  								 fieldnum,
  								 tupDesc,
! 								 sop->resnull);
  }
  
  /*
***************
*** 2534,2557 **** ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
   * Source record is in step's result variable.
   */
  void
! ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  {
  	TupleDesc	tupDesc;
  
  	/* Lookup tupdesc if first time through or after rescan */
! 	tupDesc = get_cached_rowtype(op->d.fieldstore.fstore->resulttype, -1,
! 								 op->d.fieldstore.argdesc, econtext);
  
  	/* Check that current tupdesc doesn't have more fields than we allocated */
! 	if (unlikely(tupDesc->natts > op->d.fieldstore.ncolumns))
  		elog(ERROR, "too many columns in composite type %u",
! 			 op->d.fieldstore.fstore->resulttype);
  
! 	if (*op->resnull)
  	{
  		/* Convert null input tuple into an all-nulls row */
! 		memset(op->d.fieldstore.nulls, true,
! 			   op->d.fieldstore.ncolumns * sizeof(bool));
  	}
  	else
  	{
--- 2687,2711 ----
   * Source record is in step's result variable.
   */
  void
! ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep_fieldstore *sop,
!                          ExprContext *econtext)
  {
  	TupleDesc	tupDesc;
  
  	/* Lookup tupdesc if first time through or after rescan */
! 	tupDesc = get_cached_rowtype(sop->fstore->resulttype, -1,
! 								 sop->argdesc, econtext);
  
  	/* Check that current tupdesc doesn't have more fields than we allocated */
! 	if (unlikely(tupDesc->natts > sop->ncolumns))
  		elog(ERROR, "too many columns in composite type %u",
! 			 sop->fstore->resulttype);
  
! 	if (*sop->resnull)
  	{
  		/* Convert null input tuple into an all-nulls row */
! 		memset(sop->nulls, true,
! 			   sop->ncolumns * sizeof(bool));
  	}
  	else
  	{
***************
*** 2559,2565 **** ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econte
  		 * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We
  		 * set all the fields in the struct just in case.
  		 */
! 		Datum		tupDatum = *op->resvalue;
  		HeapTupleHeader tuphdr;
  		HeapTupleData tmptup;
  
--- 2713,2719 ----
  		 * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We
  		 * set all the fields in the struct just in case.
  		 */
! 		Datum		tupDatum = *sop->resvalue;
  		HeapTupleHeader tuphdr;
  		HeapTupleData tmptup;
  
***************
*** 2570,2577 **** ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econte
  		tmptup.t_data = tuphdr;
  
  		heap_deform_tuple(&tmptup, tupDesc,
! 						  op->d.fieldstore.values,
! 						  op->d.fieldstore.nulls);
  	}
  }
  
--- 2724,2731 ----
  		tmptup.t_data = tuphdr;
  
  		heap_deform_tuple(&tmptup, tupDesc,
! 						  sop->values,
! 						  sop->nulls);
  	}
  }
  
***************
*** 2580,2596 **** ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econte
   * FieldStore expression has been evaluated.
   */
  void
! ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  {
  	HeapTuple	tuple;
  
  	/* argdesc should already be valid from the DeForm step */
! 	tuple = heap_form_tuple(*op->d.fieldstore.argdesc,
! 							op->d.fieldstore.values,
! 							op->d.fieldstore.nulls);
  
! 	*op->resvalue = HeapTupleGetDatum(tuple);
! 	*op->resnull = false;
  }
  
  /*
--- 2734,2751 ----
   * FieldStore expression has been evaluated.
   */
  void
! ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep_fieldstore *sop,
!                        ExprContext *econtext)
  {
  	HeapTuple	tuple;
  
  	/* argdesc should already be valid from the DeForm step */
! 	tuple = heap_form_tuple(*sop->argdesc,
! 							sop->values,
! 							sop->nulls);
  
! 	*sop->resvalue = HeapTupleGetDatum(tuple);
! 	*sop->resnull = false;
  }
  
  /*
***************
*** 2605,2613 **** ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext
   * lowerindex[] for use later.
   */
  bool
! ExecEvalArrayRefSubscript(ExprState *state, ExprEvalStep *op)
  {
! 	ArrayRefState *arefstate = op->d.arrayref_subscript.state;
  	int		   *indexes;
  	int			off;
  
--- 2760,2769 ----
   * lowerindex[] for use later.
   */
  bool
! ExecEvalArrayRefSubscript(ExprState *state,
!                           ExprEvalStep_arrayref_subscript *sop)
  {
! 	ArrayRefState *arefstate = sop->state;
  	int		   *indexes;
  	int			off;
  
***************
*** 2617,2633 **** ExecEvalArrayRefSubscript(ExprState *state, ExprEvalStep *op)
  		if (arefstate->isassignment)
  			ereport(ERROR,
  					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
! 					 errmsg("array subscript in assignment must not be null")));
! 		*op->resnull = true;
  		return false;
  	}
  
  	/* Convert datum to int, save in appropriate place */
! 	if (op->d.arrayref_subscript.isupper)
  		indexes = arefstate->upperindex;
  	else
  		indexes = arefstate->lowerindex;
! 	off = op->d.arrayref_subscript.off;
  
  	indexes[off] = DatumGetInt32(arefstate->subscriptvalue);
  
--- 2773,2789 ----
  		if (arefstate->isassignment)
  			ereport(ERROR,
  					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
! 				    errmsg("array subscript in assignment must not be null")));
! 		*sop->resnull = true;
  		return false;
  	}
  
  	/* Convert datum to int, save in appropriate place */
! 	if (sop->isupper)
  		indexes = arefstate->upperindex;
  	else
  		indexes = arefstate->lowerindex;
! 	off = sop->off;
  
  	indexes[off] = DatumGetInt32(arefstate->subscriptvalue);
  
***************
*** 2640,2677 **** ExecEvalArrayRefSubscript(ExprState *state, ExprEvalStep *op)
   * Source array is in step's result variable.
   */
  void
! ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep *op)
  {
! 	ArrayRefState *arefstate = op->d.arrayref.state;
  
  	/* Should not get here if source array (or any subscript) is null */
! 	Assert(!(*op->resnull));
  
  	if (arefstate->numlower == 0)
  	{
  		/* Scalar case */
! 		*op->resvalue = array_get_element(*op->resvalue,
! 										  arefstate->numupper,
! 										  arefstate->upperindex,
! 										  arefstate->refattrlength,
! 										  arefstate->refelemlength,
! 										  arefstate->refelembyval,
! 										  arefstate->refelemalign,
! 										  op->resnull);
  	}
  	else
  	{
  		/* Slice case */
! 		*op->resvalue = array_get_slice(*op->resvalue,
! 										arefstate->numupper,
! 										arefstate->upperindex,
! 										arefstate->lowerindex,
! 										arefstate->upperprovided,
! 										arefstate->lowerprovided,
! 										arefstate->refattrlength,
! 										arefstate->refelemlength,
! 										arefstate->refelembyval,
! 										arefstate->refelemalign);
  	}
  }
  
--- 2796,2833 ----
   * Source array is in step's result variable.
   */
  void
! ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep_arrayref *sop)
  {
! 	ArrayRefState *arefstate = sop->state;
  
  	/* Should not get here if source array (or any subscript) is null */
! 	Assert(!(*sop->resnull));
  
  	if (arefstate->numlower == 0)
  	{
  		/* Scalar case */
! 		*sop->resvalue = array_get_element(*sop->resvalue,
! 										   arefstate->numupper,
! 										   arefstate->upperindex,
! 										   arefstate->refattrlength,
! 										   arefstate->refelemlength,
! 										   arefstate->refelembyval,
! 										   arefstate->refelemalign,
! 										   sop->resnull);
  	}
  	else
  	{
  		/* Slice case */
! 		*sop->resvalue = array_get_slice(*sop->resvalue,
! 										 arefstate->numupper,
! 										 arefstate->upperindex,
! 										 arefstate->lowerindex,
! 										 arefstate->upperprovided,
! 										 arefstate->lowerprovided,
! 										 arefstate->refattrlength,
! 										 arefstate->refelemlength,
! 										 arefstate->refelembyval,
! 										 arefstate->refelemalign);
  	}
  }
  
***************
*** 2682,2692 **** ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep *op)
   * ArrayRefState's prevvalue/prevnull fields.
   */
  void
! ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op)
  {
! 	ArrayRefState *arefstate = op->d.arrayref.state;
  
! 	if (*op->resnull)
  	{
  		/* whole array is null, so any element or slice is too */
  		arefstate->prevvalue = (Datum) 0;
--- 2838,2848 ----
   * ArrayRefState's prevvalue/prevnull fields.
   */
  void
! ExecEvalArrayRefOld(ExprState *state, ExprEvalStep_arrayref *sop)
  {
! 	ArrayRefState *arefstate = sop->state;
  
! 	if (*sop->resnull)
  	{
  		/* whole array is null, so any element or slice is too */
  		arefstate->prevvalue = (Datum) 0;
***************
*** 2695,2701 **** ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op)
  	else if (arefstate->numlower == 0)
  	{
  		/* Scalar case */
! 		arefstate->prevvalue = array_get_element(*op->resvalue,
  												 arefstate->numupper,
  												 arefstate->upperindex,
  												 arefstate->refattrlength,
--- 2851,2857 ----
  	else if (arefstate->numlower == 0)
  	{
  		/* Scalar case */
! 		arefstate->prevvalue = array_get_element(*sop->resvalue,
  												 arefstate->numupper,
  												 arefstate->upperindex,
  												 arefstate->refattrlength,
***************
*** 2708,2714 **** ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op)
  	{
  		/* Slice case */
  		/* this is currently unreachable */
! 		arefstate->prevvalue = array_get_slice(*op->resvalue,
  											   arefstate->numupper,
  											   arefstate->upperindex,
  											   arefstate->lowerindex,
--- 2864,2870 ----
  	{
  		/* Slice case */
  		/* this is currently unreachable */
! 		arefstate->prevvalue = array_get_slice(*sop->resvalue,
  											   arefstate->numupper,
  											   arefstate->upperindex,
  											   arefstate->lowerindex,
***************
*** 2729,2737 **** ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op)
   * ArrayRefState's replacevalue/replacenull.
   */
  void
! ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep *op)
  {
! 	ArrayRefState *arefstate = op->d.arrayref.state;
  
  	/*
  	 * For an assignment to a fixed-length array type, both the original array
--- 2885,2893 ----
   * ArrayRefState's replacevalue/replacenull.
   */
  void
! ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep_arrayref *sop)
  {
! 	ArrayRefState *arefstate = sop->state;
  
  	/*
  	 * For an assignment to a fixed-length array type, both the original array
***************
*** 2740,2746 **** ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep *op)
  	 */
  	if (arefstate->refattrlength > 0)	/* fixed-length array? */
  	{
! 		if (*op->resnull || arefstate->replacenull)
  			return;
  	}
  
--- 2896,2902 ----
  	 */
  	if (arefstate->refattrlength > 0)	/* fixed-length array? */
  	{
! 		if (*sop->resnull || arefstate->replacenull)
  			return;
  	}
  
***************
*** 2750,2789 **** ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep *op)
  	 * element will result in a singleton array value.  It does not matter
  	 * whether the new element is NULL.
  	 */
! 	if (*op->resnull)
  	{
! 		*op->resvalue = PointerGetDatum(construct_empty_array(arefstate->refelemtype));
! 		*op->resnull = false;
  	}
  
  	if (arefstate->numlower == 0)
  	{
  		/* Scalar case */
! 		*op->resvalue = array_set_element(*op->resvalue,
! 										  arefstate->numupper,
! 										  arefstate->upperindex,
! 										  arefstate->replacevalue,
! 										  arefstate->replacenull,
! 										  arefstate->refattrlength,
! 										  arefstate->refelemlength,
! 										  arefstate->refelembyval,
! 										  arefstate->refelemalign);
  	}
  	else
  	{
  		/* Slice case */
! 		*op->resvalue = array_set_slice(*op->resvalue,
! 										arefstate->numupper,
! 										arefstate->upperindex,
! 										arefstate->lowerindex,
! 										arefstate->upperprovided,
! 										arefstate->lowerprovided,
! 										arefstate->replacevalue,
! 										arefstate->replacenull,
! 										arefstate->refattrlength,
! 										arefstate->refelemlength,
! 										arefstate->refelembyval,
! 										arefstate->refelemalign);
  	}
  }
  
--- 2906,2945 ----
  	 * element will result in a singleton array value.  It does not matter
  	 * whether the new element is NULL.
  	 */
! 	if (*sop->resnull)
  	{
! 		*sop->resvalue = PointerGetDatum(construct_empty_array(arefstate->refelemtype));
! 		*sop->resnull = false;
  	}
  
  	if (arefstate->numlower == 0)
  	{
  		/* Scalar case */
! 		*sop->resvalue = array_set_element(*sop->resvalue,
! 										   arefstate->numupper,
! 										   arefstate->upperindex,
! 										   arefstate->replacevalue,
! 										   arefstate->replacenull,
! 										   arefstate->refattrlength,
! 										   arefstate->refelemlength,
! 										   arefstate->refelembyval,
! 										   arefstate->refelemalign);
  	}
  	else
  	{
  		/* Slice case */
! 		*sop->resvalue = array_set_slice(*sop->resvalue,
! 										 arefstate->numupper,
! 										 arefstate->upperindex,
! 										 arefstate->lowerindex,
! 										 arefstate->upperprovided,
! 										 arefstate->lowerprovided,
! 										 arefstate->replacevalue,
! 										 arefstate->replacenull,
! 										 arefstate->refattrlength,
! 										 arefstate->refelemlength,
! 										 arefstate->refelembyval,
! 										 arefstate->refelemalign);
  	}
  }
  
***************
*** 2794,2802 **** ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep *op)
   * Source record is in step's result variable.
   */
  void
! ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  {
! 	ConvertRowtypeExpr *convert = op->d.convert_rowtype.convert;
  	HeapTuple	result;
  	Datum		tupDatum;
  	HeapTupleHeader tuple;
--- 2950,2959 ----
   * Source record is in step's result variable.
   */
  void
! ExecEvalConvertRowtype(ExprState *state, ExprEvalStep_convert_rowtype *sop,
!                        ExprContext *econtext)
  {
! 	ConvertRowtypeExpr *convert = sop->convert;
  	HeapTuple	result;
  	Datum		tupDatum;
  	HeapTupleHeader tuple;
***************
*** 2805,2834 **** ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
  				outdesc;
  
  	/* NULL in -> NULL out */
! 	if (*op->resnull)
  		return;
  
! 	tupDatum = *op->resvalue;
  	tuple = DatumGetHeapTupleHeader(tupDatum);
  
  	/* Lookup tupdescs if first time through or after rescan */
! 	if (op->d.convert_rowtype.indesc == NULL)
  	{
  		get_cached_rowtype(exprType((Node *) convert->arg), -1,
! 						   &op->d.convert_rowtype.indesc,
  						   econtext);
! 		op->d.convert_rowtype.initialized = false;
  	}
! 	if (op->d.convert_rowtype.outdesc == NULL)
  	{
  		get_cached_rowtype(convert->resulttype, -1,
! 						   &op->d.convert_rowtype.outdesc,
  						   econtext);
! 		op->d.convert_rowtype.initialized = false;
  	}
  
! 	indesc = op->d.convert_rowtype.indesc;
! 	outdesc = op->d.convert_rowtype.outdesc;
  
  	/*
  	 * We used to be able to assert that incoming tuples are marked with
--- 2962,2991 ----
  				outdesc;
  
  	/* NULL in -> NULL out */
! 	if (*sop->resnull)
  		return;
  
! 	tupDatum = *sop->resvalue;
  	tuple = DatumGetHeapTupleHeader(tupDatum);
  
  	/* Lookup tupdescs if first time through or after rescan */
! 	if (sop->indesc == NULL)
  	{
  		get_cached_rowtype(exprType((Node *) convert->arg), -1,
! 						   &sop->indesc,
  						   econtext);
! 		sop->initialized = false;
  	}
! 	if (sop->outdesc == NULL)
  	{
  		get_cached_rowtype(convert->resulttype, -1,
! 						   &sop->outdesc,
  						   econtext);
! 		sop->initialized = false;
  	}
  
! 	indesc = sop->indesc;
! 	outdesc = sop->outdesc;
  
  	/*
  	 * We used to be able to assert that incoming tuples are marked with
***************
*** 2840,2846 **** ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
  		   HeapTupleHeaderGetTypeId(tuple) == RECORDOID);
  
  	/* if first time through, initialize conversion map */
! 	if (!op->d.convert_rowtype.initialized)
  	{
  		MemoryContext old_cxt;
  
--- 2997,3003 ----
  		   HeapTupleHeaderGetTypeId(tuple) == RECORDOID);
  
  	/* if first time through, initialize conversion map */
! 	if (!sop->initialized)
  	{
  		MemoryContext old_cxt;
  
***************
*** 2848,2857 **** ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
  		old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
  
  		/* prepare map from old to new attribute numbers */
! 		op->d.convert_rowtype.map =
  			convert_tuples_by_name(indesc, outdesc,
  								   gettext_noop("could not convert row type"));
! 		op->d.convert_rowtype.initialized = true;
  
  		MemoryContextSwitchTo(old_cxt);
  	}
--- 3005,3014 ----
  		old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
  
  		/* prepare map from old to new attribute numbers */
! 		sop->map =
  			convert_tuples_by_name(indesc, outdesc,
  								   gettext_noop("could not convert row type"));
! 		sop->initialized = true;
  
  		MemoryContextSwitchTo(old_cxt);
  	}
***************
*** 2860,2871 **** ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
  	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
  	tmptup.t_data = tuple;
  
! 	if (op->d.convert_rowtype.map != NULL)
  	{
  		/* Full conversion with attribute rearrangement needed */
! 		result = do_convert_tuple(&tmptup, op->d.convert_rowtype.map);
  		/* Result already has appropriate composite-datum header fields */
! 		*op->resvalue = HeapTupleGetDatum(result);
  	}
  	else
  	{
--- 3017,3028 ----
  	tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
  	tmptup.t_data = tuple;
  
! 	if (sop->map != NULL)
  	{
  		/* Full conversion with attribute rearrangement needed */
! 		result = do_convert_tuple(&tmptup, sop->map);
  		/* Result already has appropriate composite-datum header fields */
! 		*sop->resvalue = HeapTupleGetDatum(result);
  	}
  	else
  	{
***************
*** 2879,2885 **** ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
  		 * datum, so it shouldn't contain any.  So heap_copy_tuple_as_datum()
  		 * is overkill here, but its check for external fields is cheap.
  		 */
! 		*op->resvalue = heap_copy_tuple_as_datum(&tmptup, outdesc);
  	}
  }
  
--- 3036,3042 ----
  		 * datum, so it shouldn't contain any.  So heap_copy_tuple_as_datum()
  		 * is overkill here, but its check for external fields is cheap.
  		 */
! 		*sop->resvalue = heap_copy_tuple_as_datum(&tmptup, outdesc);
  	}
  }
  
***************
*** 2894,2904 **** ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext
   * we short-circuit as soon as the result is known.
   */
  void
! ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
  {
! 	FunctionCallInfo fcinfo = op->d.scalararrayop.fcinfo_data;
! 	bool		useOr = op->d.scalararrayop.useOr;
! 	bool		strictfunc = op->d.scalararrayop.finfo->fn_strict;
  	ArrayType  *arr;
  	int			nitems;
  	Datum		result;
--- 3051,3061 ----
   * we short-circuit as soon as the result is known.
   */
  void
! ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep_scalararrayop *sop)
  {
! 	FunctionCallInfo fcinfo = sop->fcinfo_data;
! 	bool		useOr = sop->useOr;
! 	bool		strictfunc = sop->finfo->fn_strict;
  	ArrayType  *arr;
  	int			nitems;
  	Datum		result;
***************
*** 2915,2925 **** ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
  	 * If the array is NULL then we return NULL --- it's not very meaningful
  	 * to do anything else, even if the operator isn't strict.
  	 */
! 	if (*op->resnull)
  		return;
  
  	/* Else okay to fetch and detoast the array */
! 	arr = DatumGetArrayTypeP(*op->resvalue);
  
  	/*
  	 * If the array is empty, we return either FALSE or TRUE per the useOr
--- 3072,3082 ----
  	 * If the array is NULL then we return NULL --- it's not very meaningful
  	 * to do anything else, even if the operator isn't strict.
  	 */
! 	if (*sop->resnull)
  		return;
  
  	/* Else okay to fetch and detoast the array */
! 	arr = DatumGetArrayTypeP(*sop->resvalue);
  
  	/*
  	 * If the array is empty, we return either FALSE or TRUE per the useOr
***************
*** 2930,2937 **** ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
  	nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
  	if (nitems <= 0)
  	{
! 		*op->resvalue = BoolGetDatum(!useOr);
! 		*op->resnull = false;
  		return;
  	}
  
--- 3087,3094 ----
  	nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
  	if (nitems <= 0)
  	{
! 		*sop->resvalue = BoolGetDatum(!useOr);
! 		*sop->resnull = false;
  		return;
  	}
  
***************
*** 2941,2947 **** ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
  	 */
  	if (fcinfo->argnull[0] && strictfunc)
  	{
! 		*op->resnull = true;
  		return;
  	}
  
--- 3098,3104 ----
  	 */
  	if (fcinfo->argnull[0] && strictfunc)
  	{
! 		*sop->resnull = true;
  		return;
  	}
  
***************
*** 2949,2966 **** ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
  	 * We arrange to look up info about the element type only once per series
  	 * of calls, assuming the element type doesn't change underneath us.
  	 */
! 	if (op->d.scalararrayop.element_type != ARR_ELEMTYPE(arr))
  	{
  		get_typlenbyvalalign(ARR_ELEMTYPE(arr),
! 							 &op->d.scalararrayop.typlen,
! 							 &op->d.scalararrayop.typbyval,
! 							 &op->d.scalararrayop.typalign);
! 		op->d.scalararrayop.element_type = ARR_ELEMTYPE(arr);
  	}
  
! 	typlen = op->d.scalararrayop.typlen;
! 	typbyval = op->d.scalararrayop.typbyval;
! 	typalign = op->d.scalararrayop.typalign;
  
  	/* Initialize result appropriately depending on useOr */
  	result = BoolGetDatum(!useOr);
--- 3106,3123 ----
  	 * We arrange to look up info about the element type only once per series
  	 * of calls, assuming the element type doesn't change underneath us.
  	 */
! 	if (sop->element_type != ARR_ELEMTYPE(arr))
  	{
  		get_typlenbyvalalign(ARR_ELEMTYPE(arr),
! 							 &sop->typlen,
! 							 &sop->typbyval,
! 							 &sop->typalign);
! 		sop->element_type = ARR_ELEMTYPE(arr);
  	}
  
! 	typlen = sop->typlen;
! 	typbyval = sop->typbyval;
! 	typalign = sop->typalign;
  
  	/* Initialize result appropriately depending on useOr */
  	result = BoolGetDatum(!useOr);
***************
*** 3000,3006 **** ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
  		else
  		{
  			fcinfo->isnull = false;
! 			thisresult = (op->d.scalararrayop.fn_addr) (fcinfo);
  		}
  
  		/* Combine results per OR or AND semantics */
--- 3157,3163 ----
  		else
  		{
  			fcinfo->isnull = false;
! 			thisresult = (sop->fn_addr) (fcinfo);
  		}
  
  		/* Combine results per OR or AND semantics */
***************
*** 3037,3075 **** ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
  		}
  	}
  
! 	*op->resvalue = result;
! 	*op->resnull = resultnull;
  }
  
  /*
   * Evaluate a NOT NULL domain constraint.
   */
  void
! ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op)
  {
! 	if (*op->resnull)
  		ereport(ERROR,
  				(errcode(ERRCODE_NOT_NULL_VIOLATION),
  				 errmsg("domain %s does not allow null values",
! 						format_type_be(op->d.domaincheck.resulttype)),
! 				 errdatatype(op->d.domaincheck.resulttype)));
  }
  
  /*
   * Evaluate a CHECK domain constraint.
   */
  void
! ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op)
  {
! 	if (!*op->d.domaincheck.checknull &&
! 		!DatumGetBool(*op->d.domaincheck.checkvalue))
  		ereport(ERROR,
  				(errcode(ERRCODE_CHECK_VIOLATION),
! 				 errmsg("value for domain %s violates check constraint \"%s\"",
! 						format_type_be(op->d.domaincheck.resulttype),
! 						op->d.domaincheck.constraintname),
! 				 errdomainconstraint(op->d.domaincheck.resulttype,
! 									 op->d.domaincheck.constraintname)));
  }
  
  /*
--- 3194,3232 ----
  		}
  	}
  
! 	*sop->resvalue = result;
! 	*sop->resnull = resultnull;
  }
  
  /*
   * Evaluate a NOT NULL domain constraint.
   */
  void
! ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep_domaincheck *sop)
  {
! 	if (*sop->resnull)
  		ereport(ERROR,
  				(errcode(ERRCODE_NOT_NULL_VIOLATION),
  				 errmsg("domain %s does not allow null values",
! 						format_type_be(sop->resulttype)),
! 				 errdatatype(sop->resulttype)));
  }
  
  /*
   * Evaluate a CHECK domain constraint.
   */
  void
! ExecEvalConstraintCheck(ExprState *state, ExprEvalStep_domaincheck *sop)
  {
! 	if (!*sop->checknull &&
! 		!DatumGetBool(*sop->checkvalue))
  		ereport(ERROR,
  				(errcode(ERRCODE_CHECK_VIOLATION),
! 			    errmsg("value for domain %s violates check constraint \"%s\"",
! 					   format_type_be(sop->resulttype),
! 					   sop->constraintname),
! 				       errdomainconstraint(sop->resulttype,
! 									       sop->constraintname)));
  }
  
  /*
***************
*** 3079,3099 **** ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op)
   * and/or argvalue/argnull arrays.
   */
  void
! ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
  {
! 	XmlExpr    *xexpr = op->d.xmlexpr.xexpr;
  	Datum		value;
  	int			i;
  
! 	*op->resnull = true;		/* until we get a result */
! 	*op->resvalue = (Datum) 0;
  
  	switch (xexpr->op)
  	{
  		case IS_XMLCONCAT:
  			{
! 				Datum	   *argvalue = op->d.xmlexpr.argvalue;
! 				bool	   *argnull = op->d.xmlexpr.argnull;
  				List	   *values = NIL;
  
  				for (i = 0; i < list_length(xexpr->args); i++)
--- 3236,3256 ----
   * and/or argvalue/argnull arrays.
   */
  void
! ExecEvalXmlExpr(ExprState *state, ExprEvalStep_xmlexpr *sop)
  {
! 	XmlExpr    *xexpr = sop->xexpr;
  	Datum		value;
  	int			i;
  
! 	*sop->resnull = true;		/* until we get a result */
! 	*sop->resvalue = (Datum) 0;
  
  	switch (xexpr->op)
  	{
  		case IS_XMLCONCAT:
  			{
! 				Datum	   *argvalue = sop->argvalue;
! 				bool	   *argnull = sop->argnull;
  				List	   *values = NIL;
  
  				for (i = 0; i < list_length(xexpr->args); i++)
***************
*** 3104,3119 **** ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
  
  				if (values != NIL)
  				{
! 					*op->resvalue = PointerGetDatum(xmlconcat(values));
! 					*op->resnull = false;
  				}
  			}
  			break;
  
  		case IS_XMLFOREST:
  			{
! 				Datum	   *argvalue = op->d.xmlexpr.named_argvalue;
! 				bool	   *argnull = op->d.xmlexpr.named_argnull;
  				StringInfoData buf;
  				ListCell   *lc;
  				ListCell   *lc2;
--- 3261,3276 ----
  
  				if (values != NIL)
  				{
! 					*sop->resvalue = PointerGetDatum(xmlconcat(values));
! 					*sop->resnull = false;
  				}
  			}
  			break;
  
  		case IS_XMLFOREST:
  			{
! 				Datum	   *argvalue = sop->named_argvalue;
! 				bool	   *argnull = sop->named_argnull;
  				StringInfoData buf;
  				ListCell   *lc;
  				ListCell   *lc2;
***************
*** 3134,3150 **** ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
  										 map_sql_value_to_xml_value(value,
  																	exprType((Node *) e), true),
  										 argname);
! 						*op->resnull = false;
  					}
  					i++;
  				}
  
! 				if (!*op->resnull)
  				{
  					text	   *result;
  
  					result = cstring_to_text_with_len(buf.data, buf.len);
! 					*op->resvalue = PointerGetDatum(result);
  				}
  
  				pfree(buf.data);
--- 3291,3307 ----
  										 map_sql_value_to_xml_value(value,
  																	exprType((Node *) e), true),
  										 argname);
! 						*sop->resnull = false;
  					}
  					i++;
  				}
  
! 				if (!*sop->resnull)
  				{
  					text	   *result;
  
  					result = cstring_to_text_with_len(buf.data, buf.len);
! 					*sop->resvalue = PointerGetDatum(result);
  				}
  
  				pfree(buf.data);
***************
*** 3152,3169 **** ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
  			break;
  
  		case IS_XMLELEMENT:
! 			*op->resvalue = PointerGetDatum(xmlelement(xexpr,
! 													   op->d.xmlexpr.named_argvalue,
! 													   op->d.xmlexpr.named_argnull,
! 													   op->d.xmlexpr.argvalue,
! 													   op->d.xmlexpr.argnull));
! 			*op->resnull = false;
  			break;
  
  		case IS_XMLPARSE:
  			{
! 				Datum	   *argvalue = op->d.xmlexpr.argvalue;
! 				bool	   *argnull = op->d.xmlexpr.argnull;
  				text	   *data;
  				bool		preserve_whitespace;
  
--- 3309,3326 ----
  			break;
  
  		case IS_XMLELEMENT:
! 			*sop->resvalue = PointerGetDatum(xmlelement(xexpr,
! 												        sop->named_argvalue,
! 												        sop->named_argnull,
! 													    sop->argvalue,
! 													    sop->argnull));
! 			*sop->resnull = false;
  			break;
  
  		case IS_XMLPARSE:
  			{
! 				Datum	   *argvalue = sop->argvalue;
! 				bool	   *argnull = sop->argnull;
  				text	   *data;
  				bool		preserve_whitespace;
  
***************
*** 3180,3189 **** ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
  				value = argvalue[1];
  				preserve_whitespace = DatumGetBool(value);
  
! 				*op->resvalue = PointerGetDatum(xmlparse(data,
! 														 xexpr->xmloption,
! 														 preserve_whitespace));
! 				*op->resnull = false;
  			}
  			break;
  
--- 3337,3346 ----
  				value = argvalue[1];
  				preserve_whitespace = DatumGetBool(value);
  
! 				*sop->resvalue = PointerGetDatum(xmlparse(data,
! 														  xexpr->xmloption,
! 													      preserve_whitespace));
! 				*sop->resnull = false;
  			}
  			break;
  
***************
*** 3197,3207 **** ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
  
  				if (xexpr->args)
  				{
! 					isnull = op->d.xmlexpr.argnull[0];
  					if (isnull)
  						arg = NULL;
  					else
! 						arg = DatumGetTextPP(op->d.xmlexpr.argvalue[0]);
  				}
  				else
  				{
--- 3354,3364 ----
  
  				if (xexpr->args)
  				{
! 					isnull = sop->argnull[0];
  					if (isnull)
  						arg = NULL;
  					else
! 						arg = DatumGetTextPP(sop->argvalue[0]);
  				}
  				else
  				{
***************
*** 3209,3225 **** ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
  					isnull = false;
  				}
  
! 				*op->resvalue = PointerGetDatum(xmlpi(xexpr->name,
! 													  arg,
! 													  isnull,
! 													  op->resnull));
  			}
  			break;
  
  		case IS_XMLROOT:
  			{
! 				Datum	   *argvalue = op->d.xmlexpr.argvalue;
! 				bool	   *argnull = op->d.xmlexpr.argnull;
  				xmltype    *data;
  				text	   *version;
  				int			standalone;
--- 3366,3382 ----
  					isnull = false;
  				}
  
! 				*sop->resvalue = PointerGetDatum(xmlpi(xexpr->name,
! 													   arg,
! 													   isnull,
! 													   sop->resnull));
  			}
  			break;
  
  		case IS_XMLROOT:
  			{
! 				Datum	   *argvalue = sop->argvalue;
! 				bool	   *argnull = sop->argnull;
  				xmltype    *data;
  				text	   *version;
  				int			standalone;
***************
*** 3239,3255 **** ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
  				Assert(!argnull[2]);	/* always present */
  				standalone = DatumGetInt32(argvalue[2]);
  
! 				*op->resvalue = PointerGetDatum(xmlroot(data,
  														version,
  														standalone));
! 				*op->resnull = false;
  			}
  			break;
  
  		case IS_XMLSERIALIZE:
  			{
! 				Datum	   *argvalue = op->d.xmlexpr.argvalue;
! 				bool	   *argnull = op->d.xmlexpr.argnull;
  
  				/* argument type is known to be xml */
  				Assert(list_length(xexpr->args) == 1);
--- 3396,3412 ----
  				Assert(!argnull[2]);	/* always present */
  				standalone = DatumGetInt32(argvalue[2]);
  
! 				*sop->resvalue = PointerGetDatum(xmlroot(data,
  														version,
  														standalone));
! 				*sop->resnull = false;
  			}
  			break;
  
  		case IS_XMLSERIALIZE:
  			{
! 				Datum	   *argvalue = sop->argvalue;
! 				bool	   *argnull = sop->argnull;
  
  				/* argument type is known to be xml */
  				Assert(list_length(xexpr->args) == 1);
***************
*** 3258,3274 **** ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
  					return;
  				value = argvalue[0];
  
! 				*op->resvalue = PointerGetDatum(
! 												xmltotext_with_xmloption(DatumGetXmlP(value),
! 																		 xexpr->xmloption));
! 				*op->resnull = false;
  			}
  			break;
  
  		case IS_DOCUMENT:
  			{
! 				Datum	   *argvalue = op->d.xmlexpr.argvalue;
! 				bool	   *argnull = op->d.xmlexpr.argnull;
  
  				/* optional argument is known to be xml */
  				Assert(list_length(xexpr->args) == 1);
--- 3415,3431 ----
  					return;
  				value = argvalue[0];
  
! 				*sop->resvalue = PointerGetDatum(
! 								                 xmltotext_with_xmloption(DatumGetXmlP(value),
! 														                  xexpr->xmloption));
! 				*sop->resnull = false;
  			}
  			break;
  
  		case IS_DOCUMENT:
  			{
! 				Datum	   *argvalue = sop->argvalue;
! 				bool	   *argnull = sop->argnull;
  
  				/* optional argument is known to be xml */
  				Assert(list_length(xexpr->args) == 1);
***************
*** 3277,3285 **** ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
  					return;
  				value = argvalue[0];
  
! 				*op->resvalue =
  					BoolGetDatum(xml_is_document(DatumGetXmlP(value)));
! 				*op->resnull = false;
  			}
  			break;
  
--- 3434,3442 ----
  					return;
  				value = argvalue[0];
  
! 				*sop->resvalue =
  					BoolGetDatum(xml_is_document(DatumGetXmlP(value)));
! 				*sop->resnull = false;
  			}
  			break;
  
***************
*** 3299,3311 **** ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
   * grouping expressions in the current grouping set.
   */
  void
! ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op)
  {
  	int			result = 0;
! 	Bitmapset  *grouped_cols = op->d.grouping_func.parent->grouped_cols;
  	ListCell   *lc;
  
! 	foreach(lc, op->d.grouping_func.clauses)
  	{
  		int			attnum = lfirst_int(lc);
  
--- 3456,3468 ----
   * grouping expressions in the current grouping set.
   */
  void
! ExecEvalGroupingFunc(ExprState *state, ExprEvalStep_grouping_func *sop)
  {
  	int			result = 0;
! 	Bitmapset  *grouped_cols = sop->parent->grouped_cols;
  	ListCell   *lc;
  
! 	foreach(lc, sop->clauses)
  	{
  		int			attnum = lfirst_int(lc);
  
***************
*** 3315,3350 **** ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op)
  			result |= 1;
  	}
  
! 	*op->resvalue = Int32GetDatum(result);
! 	*op->resnull = false;
  }
  
  /*
   * Hand off evaluation of a subplan to nodeSubplan.c
   */
  void
! ExecEvalSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  {
! 	SubPlanState *sstate = op->d.subplan.sstate;
  
  	/* could potentially be nested, so make sure there's enough stack */
  	check_stack_depth();
  
! 	*op->resvalue = ExecSubPlan(sstate, econtext, op->resnull);
  }
  
  /*
   * Hand off evaluation of an alternative subplan to nodeSubplan.c
   */
  void
! ExecEvalAlternativeSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  {
! 	AlternativeSubPlanState *asstate = op->d.alternative_subplan.asstate;
  
  	/* could potentially be nested, so make sure there's enough stack */
  	check_stack_depth();
  
! 	*op->resvalue = ExecAlternativeSubPlan(asstate, econtext, op->resnull);
  }
  
  /*
--- 3472,3510 ----
  			result |= 1;
  	}
  
! 	*sop->resvalue = Int32GetDatum(result);
! 	*sop->resnull = false;
  }
  
  /*
   * Hand off evaluation of a subplan to nodeSubplan.c
   */
  void
! ExecEvalSubPlan(ExprState *state, ExprEvalStep_subplan *sop,
!                 ExprContext *econtext)
  {
! 	SubPlanState *sstate = sop->sstate;
  
  	/* could potentially be nested, so make sure there's enough stack */
  	check_stack_depth();
  
! 	*sop->resvalue = ExecSubPlan(sstate, econtext, sop->resnull);
  }
  
  /*
   * Hand off evaluation of an alternative subplan to nodeSubplan.c
   */
  void
! ExecEvalAlternativeSubPlan(ExprState *state,
!                            ExprEvalStep_alternative_subplan *sop,
! 						   ExprContext *econtext)
  {
! 	AlternativeSubPlanState *asstate = sop->asstate;
  
  	/* could potentially be nested, so make sure there's enough stack */
  	check_stack_depth();
  
! 	*sop->resvalue = ExecAlternativeSubPlan(asstate, econtext, sop->resnull);
  }
  
  /*
***************
*** 3354,3362 **** ExecEvalAlternativeSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econ
   * with respect to given expression context.
   */
  void
! ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  {
! 	Var		   *variable = op->d.wholerow.var;
  	TupleTableSlot *slot;
  	TupleDesc	output_tupdesc;
  	MemoryContext oldcontext;
--- 3514,3523 ----
   * with respect to given expression context.
   */
  void
! ExecEvalWholeRowVar(ExprState *state, ExprEvalStep_wholerow *sop,
!                     ExprContext *econtext)
  {
! 	Var		   *variable = sop->var;
  	TupleTableSlot *slot;
  	TupleDesc	output_tupdesc;
  	MemoryContext oldcontext;
***************
*** 3388,3395 **** ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  	}
  
  	/* Apply the junkfilter if any */
! 	if (op->d.wholerow.junkFilter != NULL)
! 		slot = ExecFilterJunk(op->d.wholerow.junkFilter, slot);
  
  	/*
  	 * If first time through, obtain tuple descriptor and check compatibility.
--- 3549,3556 ----
  	}
  
  	/* Apply the junkfilter if any */
! 	if (sop->junkFilter != NULL)
! 		slot = ExecFilterJunk(sop->junkFilter, slot);
  
  	/*
  	 * If first time through, obtain tuple descriptor and check compatibility.
***************
*** 3398,3407 **** ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  	 * initialization phase, but due to using slots that's currently not
  	 * feasible.
  	 */
! 	if (op->d.wholerow.first)
  	{
  		/* optimistically assume we don't need slow path */
! 		op->d.wholerow.slow = false;
  
  		/*
  		 * If the Var identifies a named composite type, we must check that
--- 3559,3568 ----
  	 * initialization phase, but due to using slots that's currently not
  	 * feasible.
  	 */
! 	if (sop->first)
  	{
  		/* optimistically assume we don't need slow path */
! 		sop->slow = false;
  
  		/*
  		 * If the Var identifies a named composite type, we must check that
***************
*** 3457,3463 **** ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  
  				if (vattr->attlen != sattr->attlen ||
  					vattr->attalign != sattr->attalign)
! 					op->d.wholerow.slow = true; /* need to check for nulls */
  			}
  
  			/*
--- 3618,3624 ----
  
  				if (vattr->attlen != sattr->attlen ||
  					vattr->attalign != sattr->attalign)
! 					sop->slow = true; /* need to check for nulls */
  			}
  
  			/*
***************
*** 3518,3526 **** ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  		}
  
  		/* Bless the tupdesc if needed, and save it in the execution state */
! 		op->d.wholerow.tupdesc = BlessTupleDesc(output_tupdesc);
  
! 		op->d.wholerow.first = false;
  	}
  
  	/*
--- 3679,3687 ----
  		}
  
  		/* Bless the tupdesc if needed, and save it in the execution state */
! 		sop->tupdesc = BlessTupleDesc(output_tupdesc);
  
! 		sop->first = false;
  	}
  
  	/*
***************
*** 3529,3539 **** ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  	 */
  	slot_getallattrs(slot);
  
! 	if (op->d.wholerow.slow)
  	{
  		/* Check to see if any dropped attributes are non-null */
  		TupleDesc	tupleDesc = slot->tts_tupleDescriptor;
! 		TupleDesc	var_tupdesc = op->d.wholerow.tupdesc;
  		int			i;
  
  		Assert(var_tupdesc->natts == tupleDesc->natts);
--- 3690,3700 ----
  	 */
  	slot_getallattrs(slot);
  
! 	if (sop->slow)
  	{
  		/* Check to see if any dropped attributes are non-null */
  		TupleDesc	tupleDesc = slot->tts_tupleDescriptor;
! 		TupleDesc	var_tupdesc = sop->tupdesc;
  		int			i;
  
  		Assert(var_tupdesc->natts == tupleDesc->natts);
***************
*** 3570,3581 **** ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
  	/*
  	 * Label the datum with the composite type info we identified before.
  	 *
! 	 * (Note: we could skip doing this by passing op->d.wholerow.tupdesc to
  	 * the tuple build step; but that seems a tad risky so let's not.)
  	 */
! 	HeapTupleHeaderSetTypeId(dtuple, op->d.wholerow.tupdesc->tdtypeid);
! 	HeapTupleHeaderSetTypMod(dtuple, op->d.wholerow.tupdesc->tdtypmod);
  
! 	*op->resvalue = PointerGetDatum(dtuple);
! 	*op->resnull = false;
  }
--- 3731,3742 ----
  	/*
  	 * Label the datum with the composite type info we identified before.
  	 *
! 	 * (Note: we could skip doing this by passing sop->tupdesc to
  	 * the tuple build step; but that seems a tad risky so let's not.)
  	 */
! 	HeapTupleHeaderSetTypeId(dtuple, sop->tupdesc->tdtypeid);
! 	HeapTupleHeaderSetTypMod(dtuple, sop->tupdesc->tdtypmod);
  
! 	*sop->resvalue = PointerGetDatum(dtuple);
! 	*sop->resnull = false;
  }
*** a/src/include/executor/execExpr.h
--- b/src/include/executor/execExpr.h
***************
*** 217,568 **** typedef enum ExprEvalOp
  } ExprEvalOp;
  
  
  typedef struct ExprEvalStep
  {
! 	/*
! 	 * Instruction to be executed.  During instruction preparation this is an
! 	 * enum ExprEvalOp, but later it can be changed to some other type, e.g. a
! 	 * pointer for computed goto (that's why it's an intptr_t).
! 	 */
! 	intptr_t	opcode;
! 
! 	/* where to store the result of this step */
! 	Datum	   *resvalue;
! 	bool	   *resnull;
! 
! 	/*
! 	 * Inline data for the operation.  Inline data is faster to access, but
! 	 * also bloats the size of all instructions.  The union should be kept to
! 	 * no more than 40 bytes on 64-bit systems (so that the entire struct is
! 	 * no more than 64 bytes, a single cacheline on common systems).
! 	 */
! 	union
! 	{
! 		/* for EEOP_INNER/OUTER/SCAN_FETCHSOME */
! 		struct
! 		{
! 			/* attribute number up to which to fetch (inclusive) */
! 			int			last_var;
! 		}			fetch;
! 
! 		/* for EEOP_INNER/OUTER/SCAN_[SYS]VAR[_FIRST] */
! 		struct
! 		{
! 			/* attnum is attr number - 1 for regular VAR ... */
! 			/* but it's just the normal (negative) attr number for SYSVAR */
! 			int			attnum;
! 			Oid			vartype;	/* type OID of variable */
! 		}			var;
! 
! 		/* for EEOP_WHOLEROW */
! 		struct
! 		{
! 			Var		   *var;	/* original Var node in plan tree */
! 			bool		first;	/* first time through, need to initialize? */
! 			bool		slow;	/* need runtime check for nulls? */
! 			TupleDesc	tupdesc;	/* descriptor for resulting tuples */
! 			JunkFilter *junkFilter; /* JunkFilter to remove resjunk cols */
! 		}			wholerow;
! 
! 		/* for EEOP_ASSIGN_*_VAR */
! 		struct
! 		{
! 			/* target index in ExprState->resultslot->tts_values/nulls */
! 			int			resultnum;
! 			/* source attribute number - 1 */
! 			int			attnum;
! 		}			assign_var;
! 
! 		/* for EEOP_ASSIGN_TMP[_MAKE_RO] */
! 		struct
! 		{
! 			/* target index in ExprState->resultslot->tts_values/nulls */
! 			int			resultnum;
! 		}			assign_tmp;
! 
! 		/* for EEOP_CONST */
! 		struct
! 		{
! 			/* constant's value */
! 			Datum		value;
! 			bool		isnull;
! 		}			constval;
! 
! 		/* for EEOP_FUNCEXPR_* / NULLIF / DISTINCT */
! 		struct
! 		{
! 			FmgrInfo   *finfo;	/* function's lookup data */
! 			FunctionCallInfo fcinfo_data;	/* arguments etc */
! 			/* faster to access without additional indirection: */
! 			PGFunction	fn_addr;	/* actual call address */
! 			int			nargs;	/* number of arguments */
! 		}			func;
! 
! 		/* for EEOP_BOOL_*_STEP */
! 		struct
! 		{
! 			bool	   *anynull;	/* track if any input was NULL */
! 			int			jumpdone;	/* jump here if result determined */
! 		}			boolexpr;
! 
! 		/* for EEOP_QUAL */
! 		struct
! 		{
! 			int			jumpdone;	/* jump here on false or null */
! 		}			qualexpr;
! 
! 		/* for EEOP_JUMP[_CONDITION] */
! 		struct
! 		{
! 			int			jumpdone;	/* target instruction's index */
! 		}			jump;
! 
! 		/* for EEOP_NULLTEST_ROWIS[NOT]NULL */
! 		struct
! 		{
! 			/* cached tupdesc pointer - filled at runtime */
! 			TupleDesc	argdesc;
! 		}			nulltest_row;
! 
! 		/* for EEOP_PARAM_EXEC/EXTERN */
! 		struct
! 		{
! 			int			paramid;	/* numeric ID for parameter */
! 			Oid			paramtype;	/* OID of parameter's datatype */
! 		}			param;
! 
! 		/* for EEOP_CASE_TESTVAL/DOMAIN_TESTVAL */
! 		struct
! 		{
! 			Datum	   *value;	/* value to return */
! 			bool	   *isnull;
! 		}			casetest;
! 
! 		/* for EEOP_MAKE_READONLY */
! 		struct
! 		{
! 			Datum	   *value;	/* value to coerce to read-only */
! 			bool	   *isnull;
! 		}			make_readonly;
! 
! 		/* for EEOP_IOCOERCE */
! 		struct
! 		{
! 			/* lookup and call info for source type's output function */
! 			FmgrInfo   *finfo_out;
! 			FunctionCallInfo fcinfo_data_out;
! 			/* lookup and call info for result type's input function */
! 			FmgrInfo   *finfo_in;
! 			FunctionCallInfo fcinfo_data_in;
! 		}			iocoerce;
! 
! 		/* for EEOP_SQLVALUEFUNCTION */
! 		struct
! 		{
! 			SQLValueFunction *svf;
! 		}			sqlvaluefunction;
! 
! 		/* for EEOP_NEXTVALUEEXPR */
! 		struct
! 		{
! 			Oid			seqid;
! 			Oid			seqtypid;
! 		}			nextvalueexpr;
! 
! 		/* for EEOP_ARRAYEXPR */
! 		struct
! 		{
! 			Datum	   *elemvalues; /* element values get stored here */
! 			bool	   *elemnulls;
! 			int			nelems; /* length of the above arrays */
! 			Oid			elemtype;	/* array element type */
! 			int16		elemlength; /* typlen of the array element type */
! 			bool		elembyval;	/* is the element type pass-by-value? */
! 			char		elemalign;	/* typalign of the element type */
! 			bool		multidims;	/* is array expression multi-D? */
! 		}			arrayexpr;
! 
! 		/* for EEOP_ARRAYCOERCE */
! 		struct
! 		{
! 			ArrayCoerceExpr *coerceexpr;
! 			Oid			resultelemtype; /* element type of result array */
! 			FmgrInfo   *elemfunc;	/* lookup info for element coercion
! 									 * function */
! 			struct ArrayMapState *amstate;	/* workspace for array_map */
! 		}			arraycoerce;
! 
! 		/* for EEOP_ROW */
! 		struct
! 		{
! 			TupleDesc	tupdesc;	/* descriptor for result tuples */
! 			/* workspace for the values constituting the row: */
! 			Datum	   *elemvalues;
! 			bool	   *elemnulls;
! 		}			row;
! 
! 		/* for EEOP_ROWCOMPARE_STEP */
! 		struct
! 		{
! 			/* lookup and call data for column comparison function */
! 			FmgrInfo   *finfo;
! 			FunctionCallInfo fcinfo_data;
! 			PGFunction	fn_addr;
! 			/* target for comparison resulting in NULL */
! 			int			jumpnull;
! 			/* target for comparison yielding inequality */
! 			int			jumpdone;
! 		}			rowcompare_step;
! 
! 		/* for EEOP_ROWCOMPARE_FINAL */
! 		struct
! 		{
! 			RowCompareType rctype;
! 		}			rowcompare_final;
! 
! 		/* for EEOP_MINMAX */
! 		struct
! 		{
! 			/* workspace for argument values */
! 			Datum	   *values;
! 			bool	   *nulls;
! 			int			nelems;
! 			/* is it GREATEST or LEAST? */
! 			MinMaxOp	op;
! 			/* lookup and call data for comparison function */
! 			FmgrInfo   *finfo;
! 			FunctionCallInfo fcinfo_data;
! 		}			minmax;
! 
! 		/* for EEOP_FIELDSELECT */
! 		struct
! 		{
! 			AttrNumber	fieldnum;	/* field number to extract */
! 			Oid			resulttype; /* field's type */
! 			/* cached tupdesc pointer - filled at runtime */
! 			TupleDesc	argdesc;
! 		}			fieldselect;
! 
! 		/* for EEOP_FIELDSTORE_DEFORM / FIELDSTORE_FORM */
! 		struct
! 		{
! 			/* original expression node */
! 			FieldStore *fstore;
! 
! 			/* cached tupdesc pointer - filled at runtime */
! 			/* note that a DEFORM and FORM pair share the same tupdesc */
! 			TupleDesc  *argdesc;
! 
! 			/* workspace for column values */
! 			Datum	   *values;
! 			bool	   *nulls;
! 			int			ncolumns;
! 		}			fieldstore;
! 
! 		/* for EEOP_ARRAYREF_SUBSCRIPT */
! 		struct
! 		{
! 			/* too big to have inline */
! 			struct ArrayRefState *state;
! 			int			off;	/* 0-based index of this subscript */
! 			bool		isupper;	/* is it upper or lower subscript? */
! 			int			jumpdone;	/* jump here on null */
! 		}			arrayref_subscript;
! 
! 		/* for EEOP_ARRAYREF_OLD / ASSIGN / FETCH */
! 		struct
! 		{
! 			/* too big to have inline */
! 			struct ArrayRefState *state;
! 		}			arrayref;
! 
! 		/* for EEOP_DOMAIN_NOTNULL / DOMAIN_CHECK */
! 		struct
! 		{
! 			/* name of constraint */
! 			char	   *constraintname;
! 			/* where the result of a CHECK constraint will be stored */
! 			Datum	   *checkvalue;
! 			bool	   *checknull;
! 			/* OID of domain type */
! 			Oid			resulttype;
! 		}			domaincheck;
! 
! 		/* for EEOP_CONVERT_ROWTYPE */
! 		struct
! 		{
! 			ConvertRowtypeExpr *convert;	/* original expression */
! 			/* these three fields are filled at runtime: */
! 			TupleDesc	indesc; /* tupdesc for input type */
! 			TupleDesc	outdesc;	/* tupdesc for output type */
! 			TupleConversionMap *map;	/* column mapping */
! 			bool		initialized;	/* initialized for current types? */
! 		}			convert_rowtype;
! 
! 		/* for EEOP_SCALARARRAYOP */
! 		struct
! 		{
! 			/* element_type/typlen/typbyval/typalign are filled at runtime */
! 			Oid			element_type;	/* InvalidOid if not yet filled */
! 			bool		useOr;	/* use OR or AND semantics? */
! 			int16		typlen; /* array element type storage info */
! 			bool		typbyval;
! 			char		typalign;
! 			FmgrInfo   *finfo;	/* function's lookup data */
! 			FunctionCallInfo fcinfo_data;	/* arguments etc */
! 			/* faster to access without additional indirection: */
! 			PGFunction	fn_addr;	/* actual call address */
! 		}			scalararrayop;
! 
! 		/* for EEOP_XMLEXPR */
! 		struct
! 		{
! 			XmlExpr    *xexpr;	/* original expression node */
! 			/* workspace for evaluating named args, if any */
! 			Datum	   *named_argvalue;
! 			bool	   *named_argnull;
! 			/* workspace for evaluating unnamed args, if any */
! 			Datum	   *argvalue;
! 			bool	   *argnull;
! 		}			xmlexpr;
! 
! 		/* for EEOP_AGGREF */
! 		struct
! 		{
! 			/* out-of-line state, modified by nodeAgg.c */
! 			AggrefExprState *astate;
! 		}			aggref;
! 
! 		/* for EEOP_GROUPING_FUNC */
! 		struct
! 		{
! 			AggState   *parent; /* parent Agg */
! 			List	   *clauses;	/* integer list of column numbers */
! 		}			grouping_func;
! 
! 		/* for EEOP_WINDOW_FUNC */
! 		struct
! 		{
! 			/* out-of-line state, modified by nodeWindowFunc.c */
! 			WindowFuncExprState *wfstate;
! 		}			window_func;
! 
! 		/* for EEOP_SUBPLAN */
! 		struct
! 		{
! 			/* out-of-line state, created by nodeSubplan.c */
! 			SubPlanState *sstate;
! 		}			subplan;
! 
! 		/* for EEOP_ALTERNATIVE_SUBPLAN */
! 		struct
! 		{
! 			/* out-of-line state, created by nodeSubplan.c */
! 			AlternativeSubPlanState *asstate;
! 		}			alternative_subplan;
! 	}			d;
  } ExprEvalStep;
  
  
  /* Non-inline data for array operations */
  typedef struct ArrayRefState
--- 217,648 ----
  } ExprEvalOp;
  
  
+ /*
+  * All ExecEvalStep* structures need to be interchangeable and start with
+  * the same fields. Use a #define to make the declarations consistent.
+  *
+  * The fields are:
+  *
+  *   opcode
+  *     Instruction to be executed.  During instruction preparation this is an
+  *     enum ExprEvalOp, but later it can be changed to some other type, e.g. a
+  *     pointer for computed goto (that's why it's an intptr_t).
+  *
+  *
+  *   resvalue
+  *   resnull
+  *     Where to store the result of the step.
+  *
+  *   nextstep
+  *     Next step to execute.
+  */
+ #define EXPR_EVAL_STEP_HEADER \
+    	intptr_t	         opcode;     \
+ 	Datum	            *resvalue;   \
+    	bool	            *resnull;    \
+    	struct ExprEvalStep *nextstep;
+ 
+ /* Generic step - all steps are castable to this type */
  typedef struct ExprEvalStep
  {
!    	EXPR_EVAL_STEP_HEADER;
  } ExprEvalStep;
  
+ /* for EEOP_INNER/OUTER/SCAN_FETCHSOME */
+ typedef struct ExprEvalStep_fetch
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* attribute number up to which to fetch (inclusive) */
+ 	int			last_var;
+ } ExprEvalStep_fetch;
+ 
+ /* for EEOP_INNER/OUTER/SCAN_[SYS]VAR[_FIRST] */
+ typedef struct ExprEvalStep_var
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* attnum is attr number - 1 for regular VAR ... */
+ 	/* but it's just the normal (negative) attr number for SYSVAR */
+ 	int			attnum;
+ 	Oid			vartype;	/* type OID of variable */
+ } ExprEvalStep_var;
+ 
+ /* for EEOP_WHOLEROW */
+ typedef struct ExprEvalStep_wholerow
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	Var		   *var;	/* original Var node in plan tree */
+ 	bool		first;	/* first time through, need to initialize? */
+ 	bool		slow;	/* need runtime check for nulls? */
+ 	TupleDesc	tupdesc;	/* descriptor for resulting tuples */
+ 	JunkFilter *junkFilter;		/* JunkFilter to remove resjunk cols */
+ } ExprEvalStep_wholerow;
+ 
+ /* for EEOP_ASSIGN_*_VAR */
+ typedef struct ExprEvalStep_assign_var
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* target index in ExprState->resultslot->tts_values/nulls */
+ 	int			resultnum;
+ 	/* source attribute number - 1 */
+ 	int			attnum;
+ } ExprEvalStep_assign_var;
+ 
+ /* for EEOP_ASSIGN_TMP[_MAKE_RO] */
+ typedef struct ExprEvalStep_assign_tmp
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* target index in ExprState->resultslot->tts_values/nulls */
+ 	int			resultnum;
+ } ExprEvalStep_assign_tmp;
+ 
+ /* for EEOP_CONST */
+ typedef struct ExprEvalStep_constval
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* constant's value */
+ 	Datum		value;
+ 	bool		isnull;
+ } ExprEvalStep_constval;
+ 
+ /* for EEOP_FUNCEXPR_* / NULLIF / DISTINCT */
+ typedef struct ExprEvalStep_func
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	FmgrInfo   *finfo;	/* function's lookup data */
+ 	FunctionCallInfo fcinfo_data;		/* arguments etc */
+ 	/* faster to access without additional indirection: */
+ 	PGFunction	fn_addr;	/* actual call address */
+ 	int			nargs;	/* number of arguments */
+ } ExprEvalStep_func;
+ 
+ /* for EEOP_BOOL_*_STEP */
+ typedef struct ExprEvalStep_boolexpr
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	bool	     *anynull;	/* track if any input was NULL */
+ 	ExprEvalStep *jumpdone;		/* jump here if result determined */
+ } ExprEvalStep_boolexpr;
+ 
+ /* for EEOP_QUAL */
+ typedef struct ExprEvalStep_qualexpr
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	ExprEvalStep *jumpdone;		/* jump here on false or null */
+ } ExprEvalStep_qualexpr;
+ 
+ /* for EEOP_JUMP[_CONDITION] */
+ typedef struct ExprEvalStep_jump
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	ExprEvalStep *jumpdone;		/* target instruction's index */
+ } ExprEvalStep_jump;
+ 
+ /* for EEOP_NULLTEST_ROWIS[NOT]NULL */
+ typedef struct ExprEvalStep_nulltest_row
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* cached tupdesc pointer - filled at runtime */
+ 	TupleDesc	argdesc;
+ } ExprEvalStep_nulltest_row;
+ 
+ /* for EEOP_PARAM_EXEC/EXTERN */
+ typedef struct ExprEvalStep_param
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	int			paramid;	/* numeric ID for parameter */
+ 	Oid			paramtype;		/* OID of parameter's datatype */
+ } ExprEvalStep_param;
+ 
+ /* for EEOP_CASE_TESTVAL/DOMAIN_TESTVAL */
+ typedef struct ExprEvalStep_casetest
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	Datum	   *value;	/* value to return */
+ 	bool	   *isnull;
+ } ExprEvalStep_casetest;
+ 
+ /* for EEOP_MAKE_READONLY */
+ typedef struct ExprEvalStep_make_readonly
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	Datum	   *value;	/* value to coerce to read-only */
+ 	bool	   *isnull;
+ } ExprEvalStep_make_readonly;
+ 
+ /* for EEOP_IOCOERCE */
+ typedef struct ExprEvalStep_iocoerce
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* lookup and call info for source type's output function */
+ 	FmgrInfo   *finfo_out;
+ 	FunctionCallInfo fcinfo_data_out;
+ 	/* lookup and call info for result type's input function */
+ 	FmgrInfo   *finfo_in;
+ 	FunctionCallInfo fcinfo_data_in;
+ } ExprEvalStep_iocoerce;
+ 
+ /* for EEOP_SQLVALUEFUNCTION */
+ typedef struct ExprEvalStep_sqlvaluefunction
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	SQLValueFunction *svf;
+ } ExprEvalStep_sqlvaluefunction;
+ 
+ /* for EEOP_NEXTVALUEEXPR */
+ typedef struct ExprEvalStep_nextvalueexpr
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	Oid			seqid;
+ 	Oid			seqtypid;
+ } ExprEvalStep_nextvalueexpr;
+ 
+ /* for EEOP_ARRAYEXPR */
+ typedef struct ExprEvalStep_arrayexpr
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	Datum	   *elemvalues;		/* element values get stored here */
+ 	bool	   *elemnulls;
+ 	int			nelems; /* length of the above arrays */
+ 	Oid			elemtype;		/* array element type */
+ 	int16		elemlength;		/* typlen of the array element type */
+ 	bool		elembyval;		/* is the element type pass-by-value? */
+ 	char		elemalign;		/* typalign of the element type */
+ 	bool		multidims;		/* is array expression multi-D? */
+ } ExprEvalStep_arrayexpr;
+ 
+ /* for EEOP_ARRAYCOERCE */
+ typedef struct ExprEvalStep_arraycoerce
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	ArrayCoerceExpr *coerceexpr;
+ 	Oid			resultelemtype; /* element type of result array */
+ 	FmgrInfo   *elemfunc;		/* lookup info for element coercion
+ 								 * function */
+ 	struct ArrayMapState *amstate;		/* workspace for array_map */
+ } ExprEvalStep_arraycoerce;
+ 
+ /* for EEOP_ROW */
+ typedef struct ExprEvalStep_row
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	TupleDesc	tupdesc;	/* descriptor for result tuples */
+ 	/* workspace for the values constituting the row: */
+ 	Datum	   *elemvalues;
+ 	bool	   *elemnulls;
+ } ExprEvalStep_row;
+ 
+ /* for EEOP_ROWCOMPARE_STEP */
+ typedef struct ExprEvalStep_rowcompare_step
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* lookup and call data for column comparison function */
+ 	FmgrInfo   *finfo;
+ 	FunctionCallInfo fcinfo_data;
+ 	PGFunction	fn_addr;
+ 	/* target for comparison resulting in NULL */
+ 	ExprEvalStep *jumpnull;
+ 	/* target for comparison yielding inequality */
+ 	ExprEvalStep *jumpdone;
+ } ExprEvalStep_rowcompare_step;
+ 
+ /* for EEOP_ROWCOMPARE_FINAL */
+ typedef struct ExprEvalStep_rowcompare_final
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	RowCompareType rctype;
+ } ExprEvalStep_rowcompare_final;
+ 
+ /* for EEOP_MINMAX */
+ typedef struct ExprEvalStep_minmax
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* workspace for argument values */
+ 	Datum	   *values;
+ 	bool	   *nulls;
+ 	int			nelems;
+ 	/* is it GREATEST or LEAST? */
+ 	MinMaxOp	op;
+ 	/* lookup and call data for comparison function */
+ 	FmgrInfo   *finfo;
+ 	FunctionCallInfo fcinfo_data;
+ } ExprEvalStep_minmax;
+ 
+ /* for EEOP_FIELDSELECT */
+ typedef struct ExprEvalStep_fieldselect
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	AttrNumber	fieldnum;		/* field number to extract */
+ 	Oid			resulttype;		/* field's type */
+ 	/* cached tupdesc pointer - filled at runtime */
+ 	TupleDesc	argdesc;
+ } ExprEvalStep_fieldselect;
+ 
+ /* for EEOP_FIELDSTORE_DEFORM / FIELDSTORE_FORM */
+ typedef struct ExprEvalStep_fieldstore
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* original expression node */
+ 	FieldStore *fstore;
+ 
+ 	/* cached tupdesc pointer - filled at runtime */
+ 	/* note that a DEFORM and FORM pair share the same tupdesc */
+ 	TupleDesc  *argdesc;
+ 
+ 	/* workspace for column values */
+ 	Datum	   *values;
+ 	bool	   *nulls;
+ 	int			ncolumns;
+ } ExprEvalStep_fieldstore;
+ 
+ /* for EEOP_ARRAYREF_SUBSCRIPT */
+ typedef struct ExprEvalStep_arrayref_subscript
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* too big to have inline */
+ 	struct ArrayRefState *state;
+ 	int			off;	/* 0-based index of this subscript */
+ 	bool		isupper;	/* is it upper or lower subscript? */
+ 	ExprEvalStep *jumpdone;		/* jump here on null */
+ } ExprEvalStep_arrayref_subscript;
+ 
+ /* for EEOP_ARRAYREF_OLD / ASSIGN / FETCH */
+ typedef struct ExprEvalStep_arrayref
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* too big to have inline */
+ 	struct ArrayRefState *state;
+ } ExprEvalStep_arrayref;
+ 
+ /* for EEOP_DOMAIN_NOTNULL / DOMAIN_CHECK */
+ typedef struct ExprEvalStep_domaincheck
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* name of constraint */
+ 	char	   *constraintname;
+ 	/* where the result of a CHECK constraint will be stored */
+ 	Datum	   *checkvalue;
+ 	bool	   *checknull;
+ 	/* OID of domain type */
+ 	Oid			resulttype;
+ } ExprEvalStep_domaincheck;
+ 
+ /* for EEOP_CONVERT_ROWTYPE */
+ typedef struct ExprEvalStep_convert_rowtype
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	ConvertRowtypeExpr *convert;		/* original expression */
+ 	/* these three fields are filled at runtime: */
+ 	TupleDesc	indesc; /* tupdesc for input type */
+ 	TupleDesc	outdesc;	/* tupdesc for output type */
+ 	TupleConversionMap *map;	/* column mapping */
+ 	bool		initialized;	/* initialized for current types? */
+ } ExprEvalStep_convert_rowtype;
+ 
+ /* for EEOP_SCALARARRAYOP */
+ typedef struct ExprEvalStep_scalararrayop
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* element_type/typlen/typbyval/typalign are filled at runtime */
+ 	Oid			element_type;	/* InvalidOid if not yet filled */
+ 	bool		useOr;	/* use OR or AND semantics? */
+ 	int16		typlen; /* array element type storage info */
+ 	bool		typbyval;
+ 	char		typalign;
+ 	FmgrInfo   *finfo;	/* function's lookup data */
+ 	FunctionCallInfo fcinfo_data;		/* arguments etc */
+ 	/* faster to access without additional indirection: */
+ 	PGFunction	fn_addr;	/* actual call address */
+ } ExprEvalStep_scalararrayop;
+ 
+ /* for EEOP_XMLEXPR */
+ typedef struct ExprEvalStep_xmlexpr
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	XmlExpr    *xexpr;	/* original expression node */
+ 	/* workspace for evaluating named args, if any */
+ 	Datum	   *named_argvalue;
+ 	bool	   *named_argnull;
+ 	/* workspace for evaluating unnamed args, if any */
+ 	Datum	   *argvalue;
+ 	bool	   *argnull;
+ } ExprEvalStep_xmlexpr;
+ 
+ /* for EEOP_AGGREF */
+ typedef struct ExprEvalStep_aggref
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* out-of-line state, modified by nodeAgg.c */
+ 	AggrefExprState *astate;
+ } ExprEvalStep_aggref;
+ 
+ /* for EEOP_GROUPING_FUNC */
+ typedef struct ExprEvalStep_grouping_func
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	AggState   *parent; /* parent Agg */
+ 	List	   *clauses;	/* integer list of column numbers */
+ } ExprEvalStep_grouping_func;
+ 
+ /* for EEOP_WINDOW_FUNC */
+ typedef struct ExprEvalStep_window_func
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* out-of-line state, modified by nodeWindowFunc.c */
+ 	WindowFuncExprState *wfstate;
+ } ExprEvalStep_window_func;
+ 
+ /* for EEOP_SUBPLAN */
+ typedef struct ExprEvalStep_subplan
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* out-of-line state, created by nodeSubplan.c */
+ 	SubPlanState *sstate;
+ } ExprEvalStep_subplan;
+ 
+ /* for EEOP_ALTERNATIVE_SUBPLAN */
+ typedef struct ExprEvalStep_alternative_subplan
+ {
+    	EXPR_EVAL_STEP_HEADER;
+ 
+ 	/* out-of-line state, created by nodeSubplan.c */
+ 	AlternativeSubPlanState *asstate;
+ } ExprEvalStep_alternative_subplan;
+ 
  
  /* Non-inline data for array operations */
  typedef struct ArrayRefState
***************
*** 609,651 **** extern ExprEvalOp ExecEvalStepOp(ExprState *state, ExprEvalStep *op);
   * execExprInterp.c, because that allows them to be used by other methods of
   * expression evaluation, reducing code duplication.
   */
! extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op,
  				  ExprContext *econtext);
! extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op,
  					ExprContext *econtext);
! extern void ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalNextValueExpr(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalRowNull(ExprState *state, ExprEvalStep *op,
  				ExprContext *econtext);
! extern void ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op,
  				   ExprContext *econtext);
! extern void ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalRow(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalMinMax(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op,
  					ExprContext *econtext);
! extern void ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op,
! 						 ExprContext *econtext);
! extern void ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op,
  					   ExprContext *econtext);
! extern bool ExecEvalArrayRefSubscript(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op,
! 					   ExprContext *econtext);
! extern void ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op);
! extern void ExecEvalSubPlan(ExprState *state, ExprEvalStep *op,
  				ExprContext *econtext);
! extern void ExecEvalAlternativeSubPlan(ExprState *state, ExprEvalStep *op,
  						   ExprContext *econtext);
! extern void ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op,
  					ExprContext *econtext);
  
  #endif							/* EXEC_EXPR_H */
--- 689,741 ----
   * execExprInterp.c, because that allows them to be used by other methods of
   * expression evaluation, reducing code duplication.
   */
! extern void ExecEvalParamExec(ExprState *state, ExprEvalStep_param *sop,
  				  ExprContext *econtext);
! extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep_param *sop,
  					ExprContext *econtext);
! extern void ExecEvalSQLValueFunction(ExprState *state,
!                     ExprEvalStep_sqlvaluefunction *sop);
! extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *sop);
! extern void ExecEvalNextValueExpr(ExprState *state, ExprEvalStep_nextvalueexpr *sop);
! extern void ExecEvalRowNull(ExprState *state, ExprEvalStep_nulltest_row *sop,
  				ExprContext *econtext);
! extern void ExecEvalRowNotNull(ExprState *state, ExprEvalStep_nulltest_row *sop,
  				   ExprContext *econtext);
! extern void ExecEvalArrayExpr(ExprState *state, ExprEvalStep_arrayexpr *sop);
! extern void ExecEvalArrayCoerce(ExprState *state,
!                     ExprEvalStep_arraycoerce *sop);
! extern void ExecEvalRow(ExprState *state, ExprEvalStep_row *sop);
! extern void ExecEvalMinMax(ExprState *state, ExprEvalStep_minmax *sop);
! extern void ExecEvalFieldSelect(ExprState *state, ExprEvalStep_fieldselect *sop,
  					ExprContext *econtext);
! extern void ExecEvalFieldStoreDeForm(ExprState *state,
!                          ExprEvalStep_fieldstore *sop, ExprContext *econtext);
! extern void ExecEvalFieldStoreForm(ExprState *state,
!                          ExprEvalStep_fieldstore *sop, ExprContext *econtext);
! extern bool ExecEvalArrayRefSubscript(ExprState *state,
!                     ExprEvalStep_arrayref_subscript *sop);
! extern void ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep_arrayref *sop);
! extern void ExecEvalArrayRefOld(ExprState *state, ExprEvalStep_arrayref *sop);
! extern void ExecEvalArrayRefAssign(ExprState *state,
!                     ExprEvalStep_arrayref *sop);
! extern void ExecEvalConvertRowtype(ExprState *state,
!                        ExprEvalStep_convert_rowtype *sop,
  					   ExprContext *econtext);
! extern void ExecEvalScalarArrayOp(ExprState *state,
!                     ExprEvalStep_scalararrayop *sop);
! extern void ExecEvalConstraintNotNull(ExprState *state,
!                     ExprEvalStep_domaincheck *sop);
! extern void ExecEvalConstraintCheck(ExprState *state,
!                     ExprEvalStep_domaincheck *sop);
! extern void ExecEvalXmlExpr(ExprState *state, ExprEvalStep_xmlexpr *sop);
! extern void ExecEvalGroupingFunc(ExprState *state,
!                     ExprEvalStep_grouping_func *sop);
! extern void ExecEvalSubPlan(ExprState *state, ExprEvalStep_subplan *sop,
  				ExprContext *econtext);
! extern void ExecEvalAlternativeSubPlan(ExprState *state,
!                            ExprEvalStep_alternative_subplan *sop,
  						   ExprContext *econtext);
! extern void ExecEvalWholeRowVar(ExprState *state, ExprEvalStep_wholerow *sop,
  					ExprContext *econtext);
  
  #endif							/* EXEC_EXPR_H */
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 72,77 **** typedef struct ExprState
--- 72,78 ----
  	 * Instructions to compute expression's return value.
  	 */
  	struct ExprEvalStep *steps;
+ 	int                  num_steps;
  
  	/*
  	 * Function that actually evaluates the expression.  This can be set to
***************
*** 86,93 **** typedef struct ExprState
  	 * XXX: following only needed during "compilation", could be thrown away.
  	 */
  
! 	int			steps_len;		/* number of steps currently */
! 	int			steps_alloc;	/* allocated length of steps array */
  
  	Datum	   *innermost_caseval;
  	bool	   *innermost_casenull;
--- 87,101 ----
  	 * XXX: following only needed during "compilation", could be thrown away.
  	 */
  
! 	struct ExprEvalStep *last_step; /* Last step created */
! 	char                *step_buffer; /* Memory buffer for steps */
! 	size_t               buffer_size; /* Size of remaining buffer */
! 	List                *adjust_jumps; /* "jumpdone" pointers to be resolved
! 	                                      when the next operator is created.
! 										  Generally, this list should be
! 										  created as a local variable and only
! 										  attached here when it is time to
! 										  resolve the pointers. */
  
  	Datum	   *innermost_caseval;
  	bool	   *innermost_casenull;
#2Andres Freund
andres@anarazel.de
In reply to: Douglas Doole (#1)
Re: Tightly packing expressions

Hi Doug,

On 2017-08-03 18:01:06 +0000, Douglas Doole wrote:

Back when you were getting ready to commit your faster expressions, I
volunteered to look at switching from an array of fixed sized steps to
tightly packed structures. (
/messages/by-id/20170314221648.jrcgh5n7ld4ej2o7@alap3.anarazel.de).
I've finally found time to make it happen.

Thanks for working on it!

Using tightly packed structures makes the expressions a lot smaller. I
instrumented some before and after builds and ran "make check" just to see
how much memory expressions were using. What I found was:

There were ~104K expressions.

Memory - bytes needed to hold the steps of the expressions
Allocated Memory - memory is allocated in blocks, this is the bytes
allocated to hold the expressions
Wasted Memory - the difference between the allocated and used memory

Original code:
Memory: min=64 max=9984 total=28232512 average=265
Allocated Memory: min=1024 max=16384 total=111171584 average=1045
Wasted Memory: min=0 max=6400 total=82939072 average=780

New code:
Memory: min=32 (50%) max=8128 (82%) total=18266712 (65%) average=175 (66%)
Allocated Memory: min=192 (19%) max=9408 (57%) total=29386176 (26%)
average=282 (27%)
Wasted Memory: min=0 (0%) max=1280 (20%) total=11119464 (13%) average=106
(14%)

That's actually not *that* big of a saving...

Another benefit of the way I did this is that the expression memory is
never reallocated. This means that it is safe for one step to point
directly to a field in another step without needing to separately palloc
storage. That should allow us to simplify the code and reduce pointer
traversals. (I haven't exploited this in the patch. I figured it would be a
task for round 2 assuming you like what I've done here.)

Yes, that's a neat benefit. Although I think it'd even better if we
could work to the point where the mutable and the unchanging data is
separately allocated, so we can at some point avoid redundant expression
"compilations".

The work was mostly mechanical. The only tricky bit was dealing with the
places where you jump to step n+1 while building step n. Since we can't
tell the address of step n+1 until it is actually built, I had to defer
resolution of the jumps. So all the interesting bits of this patch are in
ExprEvalPushStep().

I was wondering about a more general "symbol resolution" stage
anyway. Then we'd allocate individual steps during ExecInitExprRec, and
allocate the linear array after we know the exact size.

I think it'd be important to see some performance measurements,
especially for larger queries. It'd not be too surprising if the
different method of dereffing the next expression actually has a
negative performance effect, but I'm absolutely not sure of that.

Regards,

Andres

--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers