*** a/src/backend/executor/execMain.c
--- b/src/backend/executor/execMain.c
***************
*** 158,163 **** standard_ExecutorStart(QueryDesc *queryDesc, int eflags)
--- 158,164 ----
  	 */
  	estate->es_param_list_info = queryDesc->params;
  
+ 	estate->es_num_param_exec_vals = queryDesc->plannedstmt->nParamExec;
  	if (queryDesc->plannedstmt->nParamExec > 0)
  		estate->es_param_exec_vals = (ParamExecData *)
  			palloc0(queryDesc->plannedstmt->nParamExec * sizeof(ParamExecData));
***************
*** 2179,2184 **** EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree)
--- 2180,2186 ----
  	{
  		int			i = parentestate->es_plannedstmt->nParamExec;
  
+ 		estate->es_num_param_exec_vals = i;
  		estate->es_param_exec_vals = (ParamExecData *)
  			palloc0(i * sizeof(ParamExecData));
  		while (--i >= 0)
*** a/src/backend/executor/execQual.c
--- b/src/backend/executor/execQual.c
***************
*** 61,67 ****
  static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
  				 ExprContext *econtext,
  				 bool *isNull, ExprDoneCond *isDone);
- static bool isAssignmentIndirectionExpr(ExprState *exprstate);
  static Datum ExecEvalAggref(AggrefExprState *aggref,
  			   ExprContext *econtext,
  			   bool *isNull, ExprDoneCond *isDone);
--- 61,66 ----
***************
*** 78,83 **** static Datum ExecEvalWholeRowSlow(ExprState *exprstate, ExprContext *econtext,
--- 77,83 ----
  					 bool *isNull, ExprDoneCond *isDone);
  static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
  			  bool *isNull, ExprDoneCond *isDone);
+ static void enlargeParamExecVals(ExprContext *econtext, int maxparamid);
  static Datum ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext,
  				  bool *isNull, ExprDoneCond *isDone);
  static Datum ExecEvalParamExtern(ExprState *exprstate, ExprContext *econtext,
***************
*** 122,130 **** static Datum ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
  					   bool *isNull, ExprDoneCond *isDone);
  static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
  			 bool *isNull, ExprDoneCond *isDone);
- static Datum ExecEvalCaseTestExpr(ExprState *exprstate,
- 					 ExprContext *econtext,
- 					 bool *isNull, ExprDoneCond *isDone);
  static Datum ExecEvalArray(ArrayExprState *astate,
  			  ExprContext *econtext,
  			  bool *isNull, ExprDoneCond *isDone);
--- 122,127 ----
***************
*** 352,394 **** ExecEvalArrayRef(ArrayRefExprState *astate,
  	if (isAssignment)
  	{
  		Datum		sourceData;
! 		Datum		save_datum;
! 		bool		save_isNull;
  
  		/*
  		 * We might have a nested-assignment situation, in which the
  		 * refassgnexpr is itself a FieldStore or ArrayRef that needs to
  		 * obtain and modify the previous value of the array element or slice
  		 * being replaced.	If so, we have to extract that value from the
! 		 * array and pass it down via the econtext's caseValue.  It's safe to
! 		 * reuse the CASE mechanism because there cannot be a CASE between
! 		 * here and where the value would be needed, and an array assignment
! 		 * can't be within a CASE either.  (So saving and restoring the
! 		 * caseValue is just paranoia, but let's do it anyway.)
  		 *
  		 * Since fetching the old element might be a nontrivial expense, do it
  		 * only if the argument appears to actually need it.
  		 */
! 		save_datum = econtext->caseValue_datum;
! 		save_isNull = econtext->caseValue_isNull;
! 
! 		if (isAssignmentIndirectionExpr(astate->refassgnexpr))
  		{
  			if (*isNull)
  			{
  				/* whole array is null, so any element or slice is too */
! 				econtext->caseValue_datum = (Datum) 0;
! 				econtext->caseValue_isNull = true;
  			}
  			else if (lIndex == NULL)
  			{
! 				econtext->caseValue_datum = array_ref(array_source, i,
! 													  upper.indx,
! 													  astate->refattrlength,
! 													  astate->refelemlength,
! 													  astate->refelembyval,
! 													  astate->refelemalign,
! 												&econtext->caseValue_isNull);
  			}
  			else
  			{
--- 349,385 ----
  	if (isAssignment)
  	{
  		Datum		sourceData;
! 		ParamExecData *prm;
  
  		/*
  		 * We might have a nested-assignment situation, in which the
  		 * refassgnexpr is itself a FieldStore or ArrayRef that needs to
  		 * obtain and modify the previous value of the array element or slice
  		 * being replaced.	If so, we have to extract that value from the
! 		 * array and pass it down via an executor Param.
  		 *
  		 * Since fetching the old element might be a nontrivial expense, do it
  		 * only if the argument appears to actually need it.
  		 */
! 		if (arrayRef->refparamid != -1)
  		{
+ 			enlargeParamExecVals(econtext, arrayRef->refparamid);
+ 			prm = &econtext->ecxt_param_exec_vals[arrayRef->refparamid];
  			if (*isNull)
  			{
  				/* whole array is null, so any element or slice is too */
! 				prm->value = (Datum) 0;
! 				prm->isnull = true;
  			}
  			else if (lIndex == NULL)
  			{
! 				prm->value = array_ref(array_source, i,
! 									   upper.indx,
! 									   astate->refattrlength,
! 									   astate->refelemlength,
! 									   astate->refelembyval,
! 									   astate->refelemalign,
! 									   &prm->isnull);
  			}
  			else
  			{
***************
*** 398,413 **** ExecEvalArrayRef(ArrayRefExprState *astate,
  											  astate->refelemlength,
  											  astate->refelembyval,
  											  astate->refelemalign);
! 				econtext->caseValue_datum = PointerGetDatum(resultArray);
! 				econtext->caseValue_isNull = false;
  			}
  		}
- 		else
- 		{
- 			/* argument shouldn't need caseValue, but for safety set it null */
- 			econtext->caseValue_datum = (Datum) 0;
- 			econtext->caseValue_isNull = true;
- 		}
  
  		/*
  		 * Evaluate the value to be assigned into the array.
--- 389,398 ----
  											  astate->refelemlength,
  											  astate->refelembyval,
  											  astate->refelemalign);
! 				prm->value = PointerGetDatum(resultArray);
! 				prm->isnull = false;
  			}
  		}
  
  		/*
  		 * Evaluate the value to be assigned into the array.
***************
*** 417,425 **** ExecEvalArrayRef(ArrayRefExprState *astate,
  								  &eisnull,
  								  NULL);
  
- 		econtext->caseValue_datum = save_datum;
- 		econtext->caseValue_isNull = save_isNull;
- 
  		/*
  		 * For an assignment to a fixed-length array type, both the original
  		 * array and the value to be assigned into it must be non-NULL, else
--- 402,407 ----
***************
*** 481,515 **** ExecEvalArrayRef(ArrayRefExprState *astate,
  	}
  }
  
- /*
-  * Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef
-  * that might need the old element value passed down?
-  *
-  * (We could use this in ExecEvalFieldStore too, but in that case passing
-  * the old value is so cheap there's no need.)
-  */
- static bool
- isAssignmentIndirectionExpr(ExprState *exprstate)
- {
- 	if (exprstate == NULL)
- 		return false;			/* just paranoia */
- 	if (IsA(exprstate, FieldStoreState))
- 	{
- 		FieldStore *fstore = (FieldStore *) exprstate->expr;
- 
- 		if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
- 			return true;
- 	}
- 	else if (IsA(exprstate, ArrayRefExprState))
- 	{
- 		ArrayRef   *arrayRef = (ArrayRef *) exprstate->expr;
- 
- 		if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
- 			return true;
- 	}
- 	return false;
- }
- 
  /* ----------------------------------------------------------------
   *		ExecEvalAggref
   *
--- 463,468 ----
***************
*** 963,968 **** ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
--- 916,941 ----
  }
  
  /* ----------------------------------------------------------------
+  *		enlargeParamExecVals
+  *
+  *		Makes sure that econtext->exec_param_vals is large enough
+  *		to hold value for given paramid, enlarging the array if
+  *		necessary.
+  * ----------------------------------------------------------------
+  */
+ static void
+ enlargeParamExecVals(ExprContext *econtext, int maxparamid)
+ {
+ 	if (maxparamid >= econtext->num_param_exec_vals)
+ 	{
+ 		econtext->ecxt_param_exec_vals =
+ 			repalloc(econtext->ecxt_param_exec_vals,
+ 					 sizeof(ParamExecData) * (maxparamid + 1));
+ 		econtext->num_param_exec_vals = maxparamid + 1;
+ 	}
+ }
+ 
+ /* ----------------------------------------------------------------
   *		ExecEvalParamExec
   *
   *		Returns the value of a PARAM_EXEC parameter.
***************
*** 983,988 **** ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext,
--- 956,962 ----
  	 * PARAM_EXEC params (internal executor parameters) are stored in the
  	 * ecxt_param_exec_vals array, and can be accessed by array index.
  	 */
+ 	Assert(thisParamId < econtext->num_param_exec_vals);
  	prm = &(econtext->ecxt_param_exec_vals[thisParamId]);
  	if (prm->execPlan != NULL)
  	{
***************
*** 2769,2795 **** ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
  {
  	List	   *clauses = caseExpr->args;
  	ListCell   *clause;
- 	Datum		save_datum;
- 	bool		save_isNull;
  
  	if (isDone)
  		*isDone = ExprSingleResult;
  
  	/*
  	 * If there's a test expression, we have to evaluate it and save the value
! 	 * where the CaseTestExpr placeholders can find it. We must save and
! 	 * restore prior setting of econtext's caseValue fields, in case this node
! 	 * is itself within a larger CASE.
  	 */
- 	save_datum = econtext->caseValue_datum;
- 	save_isNull = econtext->caseValue_isNull;
- 
  	if (caseExpr->arg)
  	{
! 		econtext->caseValue_datum = ExecEvalExpr(caseExpr->arg,
! 												 econtext,
! 												 &econtext->caseValue_isNull,
! 												 NULL);
  	}
  
  	/*
--- 2743,2766 ----
  {
  	List	   *clauses = caseExpr->args;
  	ListCell   *clause;
  
  	if (isDone)
  		*isDone = ExprSingleResult;
  
  	/*
  	 * If there's a test expression, we have to evaluate it and save the value
! 	 * in the right Param slot.
  	 */
  	if (caseExpr->arg)
  	{
! 		ParamExecData *prm;
! 
! 		enlargeParamExecVals(econtext, caseExpr->paramid);
! 		prm = &econtext->ecxt_param_exec_vals[caseExpr->paramid];
! 		prm->value = ExecEvalExpr(caseExpr->arg,
! 								  econtext,
! 								  &prm->isnull,
! 								NULL);
  	}
  
  	/*
***************
*** 2814,2821 **** ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
  		 */
  		if (DatumGetBool(clause_value) && !*isNull)
  		{
- 			econtext->caseValue_datum = save_datum;
- 			econtext->caseValue_isNull = save_isNull;
  			return ExecEvalExpr(wclause->result,
  								econtext,
  								isNull,
--- 2785,2790 ----
***************
*** 2823,2831 **** ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
  		}
  	}
  
- 	econtext->caseValue_datum = save_datum;
- 	econtext->caseValue_isNull = save_isNull;
- 
  	if (caseExpr->defresult)
  	{
  		return ExecEvalExpr(caseExpr->defresult,
--- 2792,2797 ----
***************
*** 2838,2859 **** ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
  	return (Datum) 0;
  }
  
- /*
-  * ExecEvalCaseTestExpr
-  *
-  * Return the value stored by CASE.
-  */
- static Datum
- ExecEvalCaseTestExpr(ExprState *exprstate,
- 					 ExprContext *econtext,
- 					 bool *isNull, ExprDoneCond *isDone)
- {
- 	if (isDone)
- 		*isDone = ExprSingleResult;
- 	*isNull = econtext->caseValue_isNull;
- 	return econtext->caseValue_datum;
- }
- 
  /* ----------------------------------------------------------------
   *		ExecEvalArray - ARRAY[] expressions
   * ----------------------------------------------------------------
--- 2804,2809 ----
***************
*** 3936,3945 **** ExecEvalFieldStore(FieldStoreState *fstate,
  	TupleDesc	tupDesc;
  	Datum	   *values;
  	bool	   *isnull;
- 	Datum		save_datum;
- 	bool		save_isNull;
  	ListCell   *l1,
! 			   *l2;
  
  	tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);
  
--- 3886,3894 ----
  	TupleDesc	tupDesc;
  	Datum	   *values;
  	bool	   *isnull;
  	ListCell   *l1,
! 			   *l2,
! 			   *l3;
  
  	tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);
  
***************
*** 3980,4006 **** ExecEvalFieldStore(FieldStoreState *fstate,
  	/* Result is never null */
  	*isNull = false;
  
! 	save_datum = econtext->caseValue_datum;
! 	save_isNull = econtext->caseValue_isNull;
! 
! 	forboth(l1, fstate->newvals, l2, fstore->fieldnums)
  	{
  		ExprState  *newval = (ExprState *) lfirst(l1);
  		AttrNumber	fieldnum = lfirst_int(l2);
  
  		Assert(fieldnum > 0 && fieldnum <= tupDesc->natts);
  
  		/*
! 		 * Use the CaseTestExpr mechanism to pass down the old value of the
  		 * field being replaced; this is needed in case the newval is itself a
  		 * FieldStore or ArrayRef that has to obtain and modify the old value.
- 		 * It's safe to reuse the CASE mechanism because there cannot be a
- 		 * CASE between here and where the value would be needed, and a field
- 		 * assignment can't be within a CASE either.  (So saving and restoring
- 		 * the caseValue is just paranoia, but let's do it anyway.)
  		 */
! 		econtext->caseValue_datum = values[fieldnum - 1];
! 		econtext->caseValue_isNull = isnull[fieldnum - 1];
  
  		values[fieldnum - 1] = ExecEvalExpr(newval,
  											econtext,
--- 3929,3953 ----
  	/* Result is never null */
  	*isNull = false;
  
! 	forthree(l1, fstate->newvals, l2, fstore->fieldnums, l3, fstore->oldvalparamids)
  	{
  		ExprState  *newval = (ExprState *) lfirst(l1);
  		AttrNumber	fieldnum = lfirst_int(l2);
+ 		int			paramid = lfirst_int(l3);
+ 		ParamExecData *prm;
+ 
+ 		enlargeParamExecVals(econtext, paramid);
+ 		prm = &econtext->ecxt_param_exec_vals[paramid];
  
  		Assert(fieldnum > 0 && fieldnum <= tupDesc->natts);
  
  		/*
! 		 * Use an exec Param to pass down the old value of the
  		 * field being replaced; this is needed in case the newval is itself a
  		 * FieldStore or ArrayRef that has to obtain and modify the old value.
  		 */
! 		prm->value = values[fieldnum - 1];
! 		prm->isnull = isnull[fieldnum - 1];
  
  		values[fieldnum - 1] = ExecEvalExpr(newval,
  											econtext,
***************
*** 4008,4016 **** ExecEvalFieldStore(FieldStoreState *fstate,
  											NULL);
  	}
  
- 	econtext->caseValue_datum = save_datum;
- 	econtext->caseValue_isNull = save_isNull;
- 
  	tuple = heap_form_tuple(tupDesc, values, isnull);
  
  	pfree(values);
--- 3955,3960 ----
***************
*** 4256,4263 **** ExecInitExpr(Expr *node, PlanState *parent)
  			state->evalfunc = ExecEvalCoerceToDomainValue;
  			break;
  		case T_CaseTestExpr:
! 			state = (ExprState *) makeNode(ExprState);
! 			state->evalfunc = ExecEvalCaseTestExpr;
  			break;
  		case T_Aggref:
  			{
--- 4200,4207 ----
  			state->evalfunc = ExecEvalCoerceToDomainValue;
  			break;
  		case T_CaseTestExpr:
! 			/* planner should've replaced these with Params */
! 			elog(ERROR, "CaseTestExpr found in executor tree");
  			break;
  		case T_Aggref:
  			{
***************
*** 4563,4568 **** ExecInitExpr(Expr *node, PlanState *parent)
--- 4507,4513 ----
  
  				cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCase;
  				cstate->arg = ExecInitExpr(caseexpr->arg, parent);
+ 				cstate->paramid = caseexpr->paramid;
  				foreach(l, caseexpr->args)
  				{
  					CaseWhen   *when = (CaseWhen *) lfirst(l);
*** a/src/backend/executor/execUtils.c
--- b/src/backend/executor/execUtils.c
***************
*** 246,260 **** CreateExprContext(EState *estate)
  							  ALLOCSET_DEFAULT_INITSIZE,
  							  ALLOCSET_DEFAULT_MAXSIZE);
  
  	econtext->ecxt_param_exec_vals = estate->es_param_exec_vals;
  	econtext->ecxt_param_list_info = estate->es_param_list_info;
  
  	econtext->ecxt_aggvalues = NULL;
  	econtext->ecxt_aggnulls = NULL;
  
- 	econtext->caseValue_datum = (Datum) 0;
- 	econtext->caseValue_isNull = true;
- 
  	econtext->domainValue_datum = (Datum) 0;
  	econtext->domainValue_isNull = true;
  
--- 246,258 ----
  							  ALLOCSET_DEFAULT_INITSIZE,
  							  ALLOCSET_DEFAULT_MAXSIZE);
  
+ 	econtext->num_param_exec_vals = estate->es_num_param_exec_vals;
  	econtext->ecxt_param_exec_vals = estate->es_param_exec_vals;
  	econtext->ecxt_param_list_info = estate->es_param_list_info;
  
  	econtext->ecxt_aggvalues = NULL;
  	econtext->ecxt_aggnulls = NULL;
  
  	econtext->domainValue_datum = (Datum) 0;
  	econtext->domainValue_isNull = true;
  
***************
*** 317,331 **** CreateStandaloneExprContext(void)
  							  ALLOCSET_DEFAULT_INITSIZE,
  							  ALLOCSET_DEFAULT_MAXSIZE);
  
  	econtext->ecxt_param_exec_vals = NULL;
  	econtext->ecxt_param_list_info = NULL;
  
  	econtext->ecxt_aggvalues = NULL;
  	econtext->ecxt_aggnulls = NULL;
  
- 	econtext->caseValue_datum = (Datum) 0;
- 	econtext->caseValue_isNull = true;
- 
  	econtext->domainValue_datum = (Datum) 0;
  	econtext->domainValue_isNull = true;
  
--- 315,327 ----
  							  ALLOCSET_DEFAULT_INITSIZE,
  							  ALLOCSET_DEFAULT_MAXSIZE);
  
+ 	econtext->num_param_exec_vals = 0;
  	econtext->ecxt_param_exec_vals = NULL;
  	econtext->ecxt_param_list_info = NULL;
  
  	econtext->ecxt_aggvalues = NULL;
  	econtext->ecxt_aggnulls = NULL;
  
  	econtext->domainValue_datum = (Datum) 0;
  	econtext->domainValue_isNull = true;
  
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
***************
*** 1168,1173 **** _copyArrayRef(ArrayRef *from)
--- 1168,1174 ----
  	COPY_NODE_FIELD(reflowerindexpr);
  	COPY_NODE_FIELD(refexpr);
  	COPY_NODE_FIELD(refassgnexpr);
+ 	COPY_SCALAR_FIELD(refparamid);
  
  	return newnode;
  }
***************
*** 1386,1391 **** _copyFieldStore(FieldStore *from)
--- 1387,1393 ----
  	COPY_NODE_FIELD(arg);
  	COPY_NODE_FIELD(newvals);
  	COPY_NODE_FIELD(fieldnums);
+ 	COPY_NODE_FIELD(oldvalparamids);
  	COPY_SCALAR_FIELD(resulttype);
  
  	return newnode;
***************
*** 1488,1493 **** _copyCaseExpr(CaseExpr *from)
--- 1490,1496 ----
  	COPY_SCALAR_FIELD(casetype);
  	COPY_SCALAR_FIELD(casecollid);
  	COPY_NODE_FIELD(arg);
+ 	COPY_SCALAR_FIELD(paramid);
  	COPY_NODE_FIELD(args);
  	COPY_NODE_FIELD(defresult);
  	COPY_LOCATION_FIELD(location);
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
***************
*** 224,229 **** _equalArrayRef(ArrayRef *a, ArrayRef *b)
--- 224,230 ----
  	COMPARE_NODE_FIELD(reflowerindexpr);
  	COMPARE_NODE_FIELD(refexpr);
  	COMPARE_NODE_FIELD(refassgnexpr);
+ 	COMPARE_SCALAR_FIELD(refparamid);
  
  	return true;
  }
***************
*** 435,440 **** _equalFieldStore(FieldStore *a, FieldStore *b)
--- 436,442 ----
  	COMPARE_NODE_FIELD(arg);
  	COMPARE_NODE_FIELD(newvals);
  	COMPARE_NODE_FIELD(fieldnums);
+ 	COMPARE_NODE_FIELD(oldvalparamids);
  	COMPARE_SCALAR_FIELD(resulttype);
  
  	return true;
***************
*** 543,548 **** _equalCaseExpr(CaseExpr *a, CaseExpr *b)
--- 545,551 ----
  	COMPARE_SCALAR_FIELD(casetype);
  	COMPARE_SCALAR_FIELD(casecollid);
  	COMPARE_NODE_FIELD(arg);
+ 	COMPARE_SCALAR_FIELD(paramid);
  	COMPARE_NODE_FIELD(args);
  	COMPARE_NODE_FIELD(defresult);
  	COMPARE_LOCATION_FIELD(location);
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
***************
*** 989,994 **** _outArrayRef(StringInfo str, ArrayRef *node)
--- 989,995 ----
  	WRITE_NODE_FIELD(reflowerindexpr);
  	WRITE_NODE_FIELD(refexpr);
  	WRITE_NODE_FIELD(refassgnexpr);
+ 	WRITE_INT_FIELD(refparamid);
  }
  
  static void
***************
*** 1164,1169 **** _outFieldStore(StringInfo str, FieldStore *node)
--- 1165,1171 ----
  	WRITE_NODE_FIELD(arg);
  	WRITE_NODE_FIELD(newvals);
  	WRITE_NODE_FIELD(fieldnums);
+ 	WRITE_NODE_FIELD(oldvalparamids);
  	WRITE_OID_FIELD(resulttype);
  }
  
***************
*** 1236,1241 **** _outCaseExpr(StringInfo str, CaseExpr *node)
--- 1238,1244 ----
  	WRITE_OID_FIELD(casetype);
  	WRITE_OID_FIELD(casecollid);
  	WRITE_NODE_FIELD(arg);
+ 	WRITE_INT_FIELD(paramid);
  	WRITE_NODE_FIELD(args);
  	WRITE_NODE_FIELD(defresult);
  	WRITE_LOCATION_FIELD(location);
*** a/src/backend/nodes/readfuncs.c
--- b/src/backend/nodes/readfuncs.c
***************
*** 520,525 **** _readArrayRef(void)
--- 520,526 ----
  	READ_NODE_FIELD(reflowerindexpr);
  	READ_NODE_FIELD(refexpr);
  	READ_NODE_FIELD(refassgnexpr);
+ 	READ_INT_FIELD(refparamid);
  
  	READ_DONE();
  }
***************
*** 757,762 **** _readFieldStore(void)
--- 758,764 ----
  	READ_NODE_FIELD(arg);
  	READ_NODE_FIELD(newvals);
  	READ_NODE_FIELD(fieldnums);
+ 	READ_NODE_FIELD(oldvalparamids);
  	READ_OID_FIELD(resulttype);
  
  	READ_DONE();
***************
*** 859,864 **** _readCaseExpr(void)
--- 861,867 ----
  	READ_OID_FIELD(casetype);
  	READ_OID_FIELD(casecollid);
  	READ_NODE_FIELD(arg);
+ 	READ_INT_FIELD(paramid);
  	READ_NODE_FIELD(args);
  	READ_NODE_FIELD(defresult);
  	READ_LOCATION_FIELD(location);
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
***************
*** 3009,3015 **** get_column_info_for_window(PlannerInfo *root, WindowClause *wc, List *tlist,
   * calls are converted to positional notation and function default arguments
   * get inserted.  The fact that constant subexpressions get simplified is a
   * side-effect that is useful when the expression will get evaluated more than
!  * once.  Also, we must fix operator function IDs.
   *
   * Note: this must not make any damaging changes to the passed-in expression
   * tree.  (It would actually be okay to apply fix_opfuncids to it, but since
--- 3009,3016 ----
   * calls are converted to positional notation and function default arguments
   * get inserted.  The fact that constant subexpressions get simplified is a
   * side-effect that is useful when the expression will get evaluated more than
!  * once.  Also, we must fix operator function IDs. Also, this converts
!  * CaseTestExprs to executor Params.
   *
   * Note: this must not make any damaging changes to the passed-in expression
   * tree.  (It would actually be okay to apply fix_opfuncids to it, but since
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
***************
*** 57,62 **** typedef struct
--- 57,63 ----
  typedef struct
  {
  	ParamListInfo boundParams;
+ 	int			nExecParams;
  	PlannerGlobal *glob;
  	List	   *active_fns;
  	Node	   *case_val;
***************
*** 2103,2108 **** eval_const_expressions(PlannerInfo *root, Node *node)
--- 2104,2110 ----
  		context.boundParams = NULL;
  		context.glob = NULL;
  	}
+ 	context.nExecParams = 0;	/* no exec params assigned */
  	context.active_fns = NIL;	/* nothing being recursively simplified */
  	context.case_val = NULL;	/* no CASE being examined */
  	context.estimate = false;	/* safe transformations only */
***************
*** 2134,2145 **** estimate_expression_value(PlannerInfo *root, Node *node)
--- 2136,2189 ----
  	context.boundParams = root->glob->boundParams;		/* bound Params */
  	/* we do not need to mark the plan as depending on inlined functions */
  	context.glob = NULL;
+ 	context.nExecParams = 0;	/* no exec params assigned */
  	context.active_fns = NIL;	/* nothing being recursively simplified */
  	context.case_val = NULL;	/* no CASE being examined */
  	context.estimate = true;	/* unsafe transformations OK */
  	return eval_const_expressions_mutator(node, &context);
  }
  
+ /*
+  * Generate a new PARAM_EXEC Param node, for use in an expression.
+  */
+ static Node *
+ generate_expr_param(eval_const_expressions_context *context, Oid paramtype,
+ 					int32 paramtypmod, Oid paramcollation, int *paramid)
+ {
+ 	Param	   *retval;
+ 	PlannerParamItem *pitem;
+ 
+ 	/*
+ 	 * If this expression is being planned as part of a query, the expression
+ 	 * params are included in the global list of PARAM_EXEC params. If
+ 	 * this is a stand-alone expression, we just keep a counter of how many
+ 	 * we have used.
+ 	 */
+ 	if (context->glob)
+ 		*paramid = list_length(context->glob->paramlist);
+ 	else
+ 		*paramid = context->nExecParams++;
+ 
+ 	retval = makeNode(Param);
+ 	retval->paramkind = PARAM_EXEC;
+ 	retval->paramid = *paramid;
+ 	retval->paramtype = paramtype;
+ 	retval->paramtypmod = paramtypmod;
+ 	retval->paramcollid = paramcollation;
+ 	retval->location = -1;
+ 
+ 	if (context->glob)
+ 	{
+ 		pitem = makeNode(PlannerParamItem);
+ 		pitem->item = (Node *) retval;
+ 		pitem->abslevel = 0; /* XXX */
+ 
+ 		context->glob->paramlist = lappend(context->glob->paramlist, pitem);
+ 	}
+ 
+ 	return (Node *) retval;
+ }
+ 
  static Node *
  eval_const_expressions_mutator(Node *node,
  							   eval_const_expressions_context *context)
***************
*** 2722,2727 **** eval_const_expressions_mutator(Node *node,
--- 2766,2772 ----
  		CaseExpr   *newcase;
  		Node	   *save_case_val;
  		Node	   *newarg;
+ 		int			paramid;
  		List	   *newargs;
  		bool		const_true_cond;
  		Node	   *defresult = NULL;
***************
*** 2731,2745 **** eval_const_expressions_mutator(Node *node,
  		newarg = eval_const_expressions_mutator((Node *) caseexpr->arg,
  												context);
  
! 		/* Set up for contained CaseTestExpr nodes */
  		save_case_val = context->case_val;
  		if (newarg && IsA(newarg, Const))
  		{
  			context->case_val = newarg;
! 			newarg = NULL;		/* not needed anymore, see comment above */
  		}
  		else
! 			context->case_val = NULL;
  
  		/* Simplify the WHEN clauses */
  		newargs = NIL;
--- 2776,2802 ----
  		newarg = eval_const_expressions_mutator((Node *) caseexpr->arg,
  												context);
  
! 		/*
! 		 * Set up for contained CaseTestExpr nodes. If the case value is
! 		 * a constant, we can simply replace the CaseTestExpr nodes with the
! 		 * constant. Otherwise, create a executor Param to pass down the value
! 		 * at runtime from the CaseExpr. It would be legal to do the simple
! 		 * replacement for any non-volatile expression, but that's unlikely
! 		 * to be a win if the expression is any more complicated than a simple
! 		 * Const.
! 		 */
  		save_case_val = context->case_val;
  		if (newarg && IsA(newarg, Const))
  		{
  			context->case_val = newarg;
! 			newarg = NULL;		/* not needed anymore */
  		}
  		else
! 			context->case_val = generate_expr_param(context,
! 													exprType(newarg),
! 													exprTypmod(newarg),
! 													exprCollation(newarg),
! 													&paramid);
  
  		/* Simplify the WHEN clauses */
  		newargs = NIL;
***************
*** 2813,2818 **** eval_const_expressions_mutator(Node *node,
--- 2870,2876 ----
  		newcase->casetype = caseexpr->casetype;
  		newcase->casecollid = caseexpr->casecollid;
  		newcase->arg = (Expr *) newarg;
+ 		newcase->paramid = paramid;
  		newcase->args = newargs;
  		newcase->defresult = (Expr *) defresult;
  		newcase->location = caseexpr->location;
***************
*** 2821,2834 **** eval_const_expressions_mutator(Node *node,
  	if (IsA(node, CaseTestExpr))
  	{
  		/*
! 		 * If we know a constant test value for the current CASE construct,
! 		 * substitute it for the placeholder.  Else just return the
! 		 * placeholder as-is.
  		 */
! 		if (context->case_val)
! 			return copyObject(context->case_val);
  		else
! 			return copyObject(node);
  	}
  	if (IsA(node, ArrayExpr))
  	{
--- 2879,3007 ----
  	if (IsA(node, CaseTestExpr))
  	{
  		/*
! 		 * We should have a constant or a Param for the current CASE construct,
! 		 * substitute it for the placeholder.
! 		 */
! 		if (!context->case_val)
! 			elog(ERROR, "unexpected CaseTestExpr outside CASE expression");
! 		return copyObject(context->case_val);
! 	}
! 	if (IsA(node, FieldStore))
! 	{
! 		FieldStore *fstore = (FieldStore *) node;
! 		FieldStore *newfstore;
! 		List	   *newvals;
! 		List	   *oldvalparamids;
! 		Node	   *save_case_val;
! 		ListCell   *lfno,
! 				   *lnv;
! 
! 		save_case_val = context->case_val;
! 		/* Create a Param to hold the old value for each field */
! 		newvals = oldvalparamids = NIL;
! 		forboth(lnv, fstore->newvals, lfno, fstore->fieldnums)
! 		{
! 			Node *newval = lfirst(lnv);
! 			int fieldnum = lfirst_int(lfno);
! 			Node *oldval;
! 			int paramid;
! 			TupleDesc tupdesc;
! 			Form_pg_attribute attr;
! 
! 			tupdesc = lookup_rowtype_tupdesc(fstore->resulttype, -1);
! 			if (fieldnum <= 0 || fieldnum > tupdesc->natts)
! 				elog(ERROR, "invalid attno %d for row type %u", fieldnum,
! 					 fstore->resulttype);
! 
! 			attr = tupdesc->attrs[fieldnum - 1];
! 
! 			/*
! 			 * Replace CaseTestExprs in the new value with the Param which
! 			 * will hold the old value of the field at runtime.
! 			 */
! 			oldval = generate_expr_param(context, attr->atttypid,
! 										 attr->atttypmod,
! 										 attr->attcollation,
! 										 &paramid);
! 			ReleaseTupleDesc(tupdesc);
! 
! 			context->case_val = oldval;
! 			newval = eval_const_expressions_mutator((Node *) newval, context);
! 
! 			newvals = lappend(newvals, newval);
! 			oldvalparamids = lappend_int(oldvalparamids, paramid);
! 		}
! 		context->case_val = save_case_val;
! 
! 		newfstore = makeNode(FieldStore);
! 		newfstore->arg = (Expr *) eval_const_expressions_mutator((Node *) fstore->arg,
! 																 context);
! 		newfstore->newvals = newvals;
! 		newfstore->fieldnums = fstore->fieldnums;
! 		newfstore->oldvalparamids = oldvalparamids;
! 		newfstore->resulttype = fstore->resulttype;
! 
! 		return (Node *) newfstore;
! 	}
! 	if (IsA(node, ArrayRef))
! 	{
! 		ArrayRef *aref = (ArrayRef *) node;
! 		ArrayRef *newaref;
! 		int			paramid;
! 		Node	   *save_case_val;
! 		Node	   *oldval;
! 		Node	   *newrefassgnexpr;
! 
! 		/*
! 		 * We might have a nested-assignment situation, in which the
! 		 * refassgnexpr is itself a FieldStore or ArrayRef that needs to
! 		 * obtain and modify the previous value of the array element or slice
! 		 * being replaced.	If so, we have to extract that value from the
! 		 * array and pass it down via a executor Param. Parse analysis has
! 		 * used CaseTestExpr as a placeholder for it, replace it with a Param
! 		 * now.
! 		 *
! 		 * Since fetching the old element might be a nontrivial expense, do it
! 		 * only if the argument appears to actually need it.
  		 */
! 		if (aref->refassgnexpr && (IsA(aref->refassgnexpr, FieldStore) ||
! 								   IsA(aref->refassgnexpr, ArrayRef)))
! 		{
! 			/*
! 			 * Replace CaseTestExprs in the new value with the Param which
! 			 * will hold the old value of the field at runtime.
! 			 */
! 			oldval = generate_expr_param(context, aref->refelemtype,
! 										 aref->reftypmod,
! 										 aref->refcollid,
! 										 &paramid);
! 		}
  		else
! 		{
! 			oldval = NULL;
! 			paramid = -1;
! 		}
! 
! 		save_case_val = context->case_val;
! 		context->case_val = oldval;
! 		newrefassgnexpr = eval_const_expressions_mutator((Node *) aref->refassgnexpr, context);
! 		context->case_val = save_case_val;
! 
! 		newaref = makeNode(ArrayRef);
! 		newaref->refarraytype = aref->refarraytype;
! 		newaref->refelemtype = aref->refelemtype;
! 		newaref->reftypmod = aref->reftypmod;
! 		newaref->refcollid = aref->refcollid;
! 		newaref->refupperindexpr = (List *) eval_const_expressions_mutator((Node *) aref->refupperindexpr,
! 																 context);
! 		newaref->reflowerindexpr = (List *) eval_const_expressions_mutator((Node *) aref->reflowerindexpr,
! 																 context);
! 		newaref->refexpr = (Expr *) eval_const_expressions_mutator((Node *) aref->refexpr,
! 																 context);
! 		newaref->refassgnexpr = (Expr *) newrefassgnexpr;
! 		newaref->refparamid = paramid;
! 
! 		return (Node *) newaref;
  	}
  	if (IsA(node, ArrayExpr))
  	{
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 122,127 **** typedef struct ExprContext
--- 122,128 ----
  	MemoryContext ecxt_per_tuple_memory;
  
  	/* Values to substitute for Param nodes in expression */
+ 	int			num_param_exec_vals;
  	ParamExecData *ecxt_param_exec_vals;		/* for PARAM_EXEC params */
  	ParamListInfo ecxt_param_list_info; /* for other param types */
  
***************
*** 132,141 **** typedef struct ExprContext
  	Datum	   *ecxt_aggvalues; /* precomputed values for aggs/windowfuncs */
  	bool	   *ecxt_aggnulls;	/* null flags for aggs/windowfuncs */
  
- 	/* Value to substitute for CaseTestExpr nodes in expression */
- 	Datum		caseValue_datum;
- 	bool		caseValue_isNull;
- 
  	/* Value to substitute for CoerceToDomainValue nodes in expression */
  	Datum		domainValue_datum;
  	bool		domainValue_isNull;
--- 133,138 ----
***************
*** 358,363 **** typedef struct EState
--- 355,361 ----
  
  	/* Parameter info: */
  	ParamListInfo es_param_list_info;	/* values of external params */
+ 	int			es_num_param_exec_vals;
  	ParamExecData *es_param_exec_vals;	/* values of internal params */
  
  	/* Other working state: */
***************
*** 812,817 **** typedef struct CaseExprState
--- 810,816 ----
  {
  	ExprState	xprstate;
  	ExprState  *arg;			/* implicit equality comparison argument */
+ 	int			paramid;		/* exec param slot to hold the argument */
  	List	   *args;			/* the arguments (list of WHEN clauses) */
  	ExprState  *defresult;		/* the default result (ELSE clause) */
  } CaseExprState;
*** a/src/include/nodes/primnodes.h
--- b/src/include/nodes/primnodes.h
***************
*** 295,300 **** typedef struct ArrayRef
--- 295,302 ----
  								 * value */
  	Expr	   *refassgnexpr;	/* expression for the source value, or NULL if
  								 * fetch */
+ 	int			refparamid;		/* exec param to hold old value during
+ 								 * refassgnexpr execution */
  } ArrayRef;
  
  /*
***************
*** 639,644 **** typedef struct FieldStore
--- 641,647 ----
  	Expr	   *arg;			/* input tuple value */
  	List	   *newvals;		/* new value(s) for field(s) */
  	List	   *fieldnums;		/* integer list of field attnums */
+ 	List	   *oldvalparamids; /* exec params to hold old values of fields */
  	Oid			resulttype;		/* type of result (same as type of arg) */
  	/* Like RowExpr, we deliberately omit a typmod and collation here */
  } FieldStore;
***************
*** 767,772 **** typedef struct CollateExpr
--- 770,778 ----
   * evaluated only once.  Note that after parse analysis, the condition
   * expressions always yield boolean.
   *
+  * The planner will replace CaseTestExpr with a exec Param node, and set
+  * paramid.
+  *
   * Note: we can test whether a CaseExpr has been through parse analysis
   * yet by checking whether casetype is InvalidOid or not.
   *----------
***************
*** 777,782 **** typedef struct CaseExpr
--- 783,789 ----
  	Oid			casetype;		/* type of expression result */
  	Oid			casecollid;		/* OID of collation, or InvalidOid if none */
  	Expr	   *arg;			/* implicit equality comparison argument */
+ 	int			paramid;
  	List	   *args;			/* the arguments (list of WHEN clauses) */
  	Expr	   *defresult;		/* the default result (ELSE clause) */
  	int			location;		/* token location, or -1 if unknown */
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
***************
*** 5459,5467 **** exec_simple_check_node(Node *node)
  				return TRUE;
  			}
  
- 		case T_CaseTestExpr:
- 			return TRUE;
- 
  		case T_ArrayExpr:
  			{
  				ArrayExpr  *expr = (ArrayExpr *) node;
--- 5459,5464 ----
