*** 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 ----
***************
*** 82,87 **** static Datum ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext,
--- 81,88 ----
  				  bool *isNull, ExprDoneCond *isDone);
  static Datum ExecEvalParamExtern(ExprState *exprstate, ExprContext *econtext,
  					bool *isNull, ExprDoneCond *isDone);
+ static Datum ExecEvalExpressionParam(ExprState *exprstate, ExprContext *econtext,
+ 				  bool *isNull, ExprDoneCond *isDone);
  static void init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
  			MemoryContext fcacheCxt, bool needDescForSets);
  static void ShutdownFuncExpr(Datum arg);
***************
*** 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);
--- 123,128 ----
***************
*** 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
  			{
--- 350,382 ----
  	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 expression param.
  		 */
! 		if (arrayRef->refuseparam)
  		{
+ 			prm.execPlan = NULL; /* not used */
  			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,412 **** 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;
  		}
  
  		/*
--- 386,395 ----
  											  astate->refelemlength,
  											  astate->refelembyval,
  											  astate->refelemalign);
! 				prm.value = PointerGetDatum(resultArray);
! 				prm.isnull = false;
  			}
! 			econtext->expr_params = lcons(&prm, econtext->expr_params);
  		}
  
  		/*
***************
*** 417,424 **** 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
--- 400,408 ----
  								  &eisnull,
  								  NULL);
  
! 		/* pop the expression param stack if we pushed */
! 		if (arrayRef->refuseparam)
! 			econtext->expr_params = list_delete_first(econtext->expr_params);
  
  		/*
  		 * For an assignment to a fixed-length array type, both the original
***************
*** 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
   *
--- 465,470 ----
***************
*** 1046,1051 **** ExecEvalParamExtern(ExprState *exprstate, ExprContext *econtext,
--- 1001,1032 ----
  	return (Datum) 0;			/* keep compiler quiet */
  }
  
+ /* ----------------------------------------------------------------
+  *		ExecEvalExpressionParam
+  *
+  *		Returns the value of an expression parameter.
+  * ----------------------------------------------------------------
+  */
+ static Datum
+ ExecEvalExpressionParam(ExprState *exprstate, ExprContext *econtext,
+ 						bool *isNull, ExprDoneCond *isDone)
+ {
+ 	ExpressionParam *eparam = (ExpressionParam *) exprstate->expr;
+ 	ParamExecData *prm;
+ 
+ 	if (isDone)
+ 		*isDone = ExprSingleResult;
+ 
+ 	/* Expression params are stored in a stack, in econtext->expr_params */
+ 	if (eparam->levelsup >= list_length(econtext->expr_params))
+ 		elog(ERROR, "invalid expression parameter reference (%d levels up, while stack is only %d elements deep)",
+ 			 eparam->levelsup, list_length(econtext->expr_params));
+ 	prm = list_nth(econtext->expr_params, eparam->levelsup);
+ 
+ 	*isNull = prm->isnull;
+ 	return prm->value;
+ }
+ 
  
  /* ----------------------------------------------------------------
   *		ExecEvalOper / ExecEvalFunc support routines
***************
*** 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);
  	}
  
  	/*
--- 2750,2772 ----
  {
  	List	   *clauses = caseExpr->args;
  	ListCell   *clause;
! 	ParamExecData prm;
  
  	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)
  	{
! 		prm.execPlan = NULL; /* not used */
! 		prm.value = ExecEvalExpr(caseExpr->arg,
! 								 econtext,
! 								 &prm.isnull,
! 								 NULL);
! 		econtext->expr_params = lcons(&prm, econtext->expr_params);
  	}
  
  	/*
***************
*** 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,
--- 2791,2798 ----
  		 */
  		if (DatumGetBool(clause_value) && !*isNull)
  		{
! 			if (caseExpr->arg)
! 				econtext->expr_params = list_delete_first(econtext->expr_params);
  			return ExecEvalExpr(wclause->result,
  								econtext,
  								isNull,
***************
*** 2823,2830 **** ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
  		}
  	}
  
! 	econtext->caseValue_datum = save_datum;
! 	econtext->caseValue_isNull = save_isNull;
  
  	if (caseExpr->defresult)
  	{
--- 2800,2808 ----
  		}
  	}
  
! 	/* pop the expression param stack if we pushed a value */
! 	if (caseExpr->arg)
! 		econtext->expr_params = list_delete_first(econtext->expr_params);
  
  	if (caseExpr->defresult)
  	{
***************
*** 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
   * ----------------------------------------------------------------
--- 2816,2821 ----
***************
*** 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);
  
--- 3898,3906 ----
  	TupleDesc	tupDesc;
  	Datum	   *values;
  	bool	   *isnull;
  	ListCell   *l1,
  			   *l2;
+ 	ParamExecData prm;
  
  	tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone);
  
***************
*** 3980,3987 **** 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)
  	{
--- 3941,3947 ----
  	/* Result is never null */
  	*isNull = false;
  
! 	econtext->expr_params = lcons(&prm, econtext->expr_params);
  
  	forboth(l1, fstate->newvals, l2, fstore->fieldnums)
  	{
***************
*** 3991,4006 **** ExecEvalFieldStore(FieldStoreState *fstate,
  		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,
--- 3951,3963 ----
  		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.execPlan = NULL; /* not used */
! 		prm.value = values[fieldnum - 1];
! 		prm.isnull = isnull[fieldnum - 1];
  
  		values[fieldnum - 1] = ExecEvalExpr(newval,
  											econtext,
***************
*** 4008,4015 **** ExecEvalFieldStore(FieldStoreState *fstate,
  											NULL);
  	}
  
! 	econtext->caseValue_datum = save_datum;
! 	econtext->caseValue_isNull = save_isNull;
  
  	tuple = heap_form_tuple(tupDesc, values, isnull);
  
--- 3965,3971 ----
  											NULL);
  	}
  
! 	econtext->expr_params = list_delete_first(econtext->expr_params);
  
  	tuple = heap_form_tuple(tupDesc, values, isnull);
  
***************
*** 4251,4263 **** ExecInitExpr(Expr *node, PlanState *parent)
  					break;
  			}
  			break;
! 		case T_CoerceToDomainValue:
  			state = (ExprState *) makeNode(ExprState);
! 			state->evalfunc = ExecEvalCoerceToDomainValue;
  			break;
! 		case T_CaseTestExpr:
  			state = (ExprState *) makeNode(ExprState);
! 			state->evalfunc = ExecEvalCaseTestExpr;
  			break;
  		case T_Aggref:
  			{
--- 4207,4219 ----
  					break;
  			}
  			break;
! 		case T_ExpressionParam:
  			state = (ExprState *) makeNode(ExprState);
! 			state->evalfunc = ExecEvalExpressionParam;
  			break;
! 		case T_CoerceToDomainValue:
  			state = (ExprState *) makeNode(ExprState);
! 			state->evalfunc = ExecEvalCoerceToDomainValue;
  			break;
  		case T_Aggref:
  			{
*** a/src/backend/executor/execUtils.c
--- b/src/backend/executor/execUtils.c
***************
*** 252,260 **** CreateExprContext(EState *estate)
  	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;
  
--- 252,257 ----
***************
*** 323,331 **** CreateStandaloneExprContext(void)
  	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;
  
--- 320,325 ----
*** 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(refuseparam);
  
  	return newnode;
  }
***************
*** 1386,1391 **** _copyFieldStore(FieldStore *from)
--- 1387,1393 ----
  	COPY_NODE_FIELD(arg);
  	COPY_NODE_FIELD(newvals);
  	COPY_NODE_FIELD(fieldnums);
+ 	COPY_SCALAR_FIELD(useparam);
  	COPY_SCALAR_FIELD(resulttype);
  
  	return newnode;
***************
*** 1511,1526 **** _copyCaseWhen(CaseWhen *from)
  }
  
  /*
!  * _copyCaseTestExpr
   */
! static CaseTestExpr *
! _copyCaseTestExpr(CaseTestExpr *from)
  {
! 	CaseTestExpr *newnode = makeNode(CaseTestExpr);
  
  	COPY_SCALAR_FIELD(typeId);
  	COPY_SCALAR_FIELD(typeMod);
  	COPY_SCALAR_FIELD(collation);
  
  	return newnode;
  }
--- 1513,1530 ----
  }
  
  /*
!  * _copyExpressionParam
   */
! static ExpressionParam *
! _copyExpressionParam(ExpressionParam *from)
  {
! 	ExpressionParam *newnode = makeNode(ExpressionParam);
  
+ 	COPY_SCALAR_FIELD(levelsup);
  	COPY_SCALAR_FIELD(typeId);
  	COPY_SCALAR_FIELD(typeMod);
  	COPY_SCALAR_FIELD(collation);
+ 	COPY_LOCATION_FIELD(location);
  
  	return newnode;
  }
***************
*** 4042,4049 **** copyObject(void *from)
  		case T_CaseWhen:
  			retval = _copyCaseWhen(from);
  			break;
! 		case T_CaseTestExpr:
! 			retval = _copyCaseTestExpr(from);
  			break;
  		case T_ArrayExpr:
  			retval = _copyArrayExpr(from);
--- 4046,4053 ----
  		case T_CaseWhen:
  			retval = _copyCaseWhen(from);
  			break;
! 		case T_ExpressionParam:
! 			retval = _copyExpressionParam(from);
  			break;
  		case T_ArrayExpr:
  			retval = _copyArrayExpr(from);
*** 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(refuseparam);
  
  	return true;
  }
***************
*** 435,440 **** _equalFieldStore(FieldStore *a, FieldStore *b)
--- 436,442 ----
  	COMPARE_NODE_FIELD(arg);
  	COMPARE_NODE_FIELD(newvals);
  	COMPARE_NODE_FIELD(fieldnums);
+ 	COMPARE_SCALAR_FIELD(useparam);
  	COMPARE_SCALAR_FIELD(resulttype);
  
  	return true;
***************
*** 561,571 **** _equalCaseWhen(CaseWhen *a, CaseWhen *b)
  }
  
  static bool
! _equalCaseTestExpr(CaseTestExpr *a, CaseTestExpr *b)
  {
  	COMPARE_SCALAR_FIELD(typeId);
  	COMPARE_SCALAR_FIELD(typeMod);
  	COMPARE_SCALAR_FIELD(collation);
  
  	return true;
  }
--- 563,575 ----
  }
  
  static bool
! _equalExpressionParam(ExpressionParam *a, ExpressionParam *b)
  {
+ 	COMPARE_SCALAR_FIELD(levelsup);
  	COMPARE_SCALAR_FIELD(typeId);
  	COMPARE_SCALAR_FIELD(typeMod);
  	COMPARE_SCALAR_FIELD(collation);
+ 	COMPARE_LOCATION_FIELD(location);
  
  	return true;
  }
***************
*** 2608,2615 **** equal(void *a, void *b)
  		case T_CaseWhen:
  			retval = _equalCaseWhen(a, b);
  			break;
! 		case T_CaseTestExpr:
! 			retval = _equalCaseTestExpr(a, b);
  			break;
  		case T_ArrayExpr:
  			retval = _equalArrayExpr(a, b);
--- 2612,2619 ----
  		case T_CaseWhen:
  			retval = _equalCaseWhen(a, b);
  			break;
! 		case T_ExpressionParam:
! 			retval = _equalExpressionParam(a, b);
  			break;
  		case T_ArrayExpr:
  			retval = _equalArrayExpr(a, b);
*** a/src/backend/nodes/nodeFuncs.c
--- b/src/backend/nodes/nodeFuncs.c
***************
*** 180,187 **** exprType(Node *expr)
  		case T_CaseExpr:
  			type = ((CaseExpr *) expr)->casetype;
  			break;
! 		case T_CaseTestExpr:
! 			type = ((CaseTestExpr *) expr)->typeId;
  			break;
  		case T_ArrayExpr:
  			type = ((ArrayExpr *) expr)->array_typeid;
--- 180,187 ----
  		case T_CaseExpr:
  			type = ((CaseExpr *) expr)->casetype;
  			break;
! 		case T_ExpressionParam:
! 			type = ((ExpressionParam *) expr)->typeId;
  			break;
  		case T_ArrayExpr:
  			type = ((ArrayExpr *) expr)->array_typeid;
***************
*** 365,372 **** exprTypmod(Node *expr)
  				return typmod;
  			}
  			break;
! 		case T_CaseTestExpr:
! 			return ((CaseTestExpr *) expr)->typeMod;
  		case T_ArrayExpr:
  			{
  				/*
--- 365,372 ----
  				return typmod;
  			}
  			break;
! 		case T_ExpressionParam:
! 			return ((ExpressionParam *) expr)->typeMod;
  		case T_ArrayExpr:
  			{
  				/*
***************
*** 756,763 **** exprCollation(Node *expr)
  		case T_CaseExpr:
  			coll = ((CaseExpr *) expr)->casecollid;
  			break;
! 		case T_CaseTestExpr:
! 			coll = ((CaseTestExpr *) expr)->collation;
  			break;
  		case T_ArrayExpr:
  			coll = ((ArrayExpr *) expr)->array_collid;
--- 756,763 ----
  		case T_CaseExpr:
  			coll = ((CaseExpr *) expr)->casecollid;
  			break;
! 		case T_ExpressionParam:
! 			coll = ((ExpressionParam *) expr)->collation;
  			break;
  		case T_ArrayExpr:
  			coll = ((ArrayExpr *) expr)->array_collid;
***************
*** 1525,1531 **** expression_tree_walker(Node *node,
  		case T_Const:
  		case T_Param:
  		case T_CoerceToDomainValue:
! 		case T_CaseTestExpr:
  		case T_SetToDefault:
  		case T_CurrentOfExpr:
  		case T_RangeTblRef:
--- 1525,1531 ----
  		case T_Const:
  		case T_Param:
  		case T_CoerceToDomainValue:
! 		case T_ExpressionParam:
  		case T_SetToDefault:
  		case T_CurrentOfExpr:
  		case T_RangeTblRef:
***************
*** 2040,2046 **** expression_tree_mutator(Node *node,
  			break;
  		case T_Param:
  		case T_CoerceToDomainValue:
! 		case T_CaseTestExpr:
  		case T_SetToDefault:
  		case T_CurrentOfExpr:
  		case T_RangeTblRef:
--- 2040,2046 ----
  			break;
  		case T_Param:
  		case T_CoerceToDomainValue:
! 		case T_ExpressionParam:
  		case T_SetToDefault:
  		case T_CurrentOfExpr:
  		case T_RangeTblRef:
*** 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_BOOL_FIELD(refuseparam);
  }
  
  static void
***************
*** 1164,1169 **** _outFieldStore(StringInfo str, FieldStore *node)
--- 1165,1171 ----
  	WRITE_NODE_FIELD(arg);
  	WRITE_NODE_FIELD(newvals);
  	WRITE_NODE_FIELD(fieldnums);
+ 	WRITE_BOOL_FIELD(useparam);
  	WRITE_OID_FIELD(resulttype);
  }
  
***************
*** 1252,1264 **** _outCaseWhen(StringInfo str, CaseWhen *node)
  }
  
  static void
! _outCaseTestExpr(StringInfo str, CaseTestExpr *node)
  {
! 	WRITE_NODE_TYPE("CASETESTEXPR");
  
  	WRITE_OID_FIELD(typeId);
  	WRITE_INT_FIELD(typeMod);
  	WRITE_OID_FIELD(collation);
  }
  
  static void
--- 1254,1268 ----
  }
  
  static void
! _outExpressionParam(StringInfo str, ExpressionParam *node)
  {
! 	WRITE_NODE_TYPE("EXPRESSIONPARAM");
  
+ 	WRITE_INT_FIELD(levelsup);
  	WRITE_OID_FIELD(typeId);
  	WRITE_INT_FIELD(typeMod);
  	WRITE_OID_FIELD(collation);
+ 	WRITE_LOCATION_FIELD(location);
  }
  
  static void
***************
*** 2878,2885 **** _outNode(StringInfo str, void *obj)
  			case T_CaseWhen:
  				_outCaseWhen(str, obj);
  				break;
! 			case T_CaseTestExpr:
! 				_outCaseTestExpr(str, obj);
  				break;
  			case T_ArrayExpr:
  				_outArrayExpr(str, obj);
--- 2882,2889 ----
  			case T_CaseWhen:
  				_outCaseWhen(str, obj);
  				break;
! 			case T_ExpressionParam:
! 				_outExpressionParam(str, obj);
  				break;
  			case T_ArrayExpr:
  				_outArrayExpr(str, obj);
*** 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_BOOL_FIELD(refuseparam);
  
  	READ_DONE();
  }
***************
*** 757,762 **** _readFieldStore(void)
--- 758,764 ----
  	READ_NODE_FIELD(arg);
  	READ_NODE_FIELD(newvals);
  	READ_NODE_FIELD(fieldnums);
+ 	READ_BOOL_FIELD(useparam);
  	READ_OID_FIELD(resulttype);
  
  	READ_DONE();
***************
*** 882,897 **** _readCaseWhen(void)
  }
  
  /*
!  * _readCaseTestExpr
   */
! static CaseTestExpr *
! _readCaseTestExpr(void)
  {
! 	READ_LOCALS(CaseTestExpr);
  
  	READ_OID_FIELD(typeId);
  	READ_INT_FIELD(typeMod);
  	READ_OID_FIELD(collation);
  
  	READ_DONE();
  }
--- 884,901 ----
  }
  
  /*
!  * _readExpressionParam
   */
! static ExpressionParam *
! _readExpressionParam(void)
  {
! 	READ_LOCALS(ExpressionParam);
  
+ 	READ_INT_FIELD(levelsup);
  	READ_OID_FIELD(typeId);
  	READ_INT_FIELD(typeMod);
  	READ_OID_FIELD(collation);
+ 	READ_LOCATION_FIELD(location);
  
  	READ_DONE();
  }
***************
*** 1314,1321 **** parseNodeString(void)
  		return_value = _readCaseExpr();
  	else if (MATCH("WHEN", 4))
  		return_value = _readCaseWhen();
! 	else if (MATCH("CASETESTEXPR", 12))
! 		return_value = _readCaseTestExpr();
  	else if (MATCH("ARRAY", 5))
  		return_value = _readArrayExpr();
  	else if (MATCH("ROW", 3))
--- 1318,1325 ----
  		return_value = _readCaseExpr();
  	else if (MATCH("WHEN", 4))
  		return_value = _readCaseWhen();
! 	else if (MATCH("EXPRESSIONPARAM", 15))
! 		return_value = _readExpressionParam();
  	else if (MATCH("ARRAY", 5))
  		return_value = _readArrayExpr();
  	else if (MATCH("ROW", 3))
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
***************
*** 59,73 **** typedef struct
  	ParamListInfo boundParams;
  	PlannerGlobal *glob;
  	List	   *active_fns;
- 	Node	   *case_val;
  	bool		estimate;
  } eval_const_expressions_context;
  
  typedef struct
  {
  	int			nargs;
  	List	   *args;
  	int		   *usecounts;
  } substitute_actual_parameters_context;
  
  typedef struct
--- 59,79 ----
  	ParamListInfo boundParams;
  	PlannerGlobal *glob;
  	List	   *active_fns;
  	bool		estimate;
  } eval_const_expressions_context;
  
  typedef struct
  {
+ 	Node *replacement;
+ 	int replacement_level;
+ } replace_expression_param_context;
+ 
+ typedef struct
+ {
  	int			nargs;
  	List	   *args;
  	int		   *usecounts;
+ 	int			expr_param_depth;
  } substitute_actual_parameters_context;
  
  typedef struct
***************
*** 129,138 **** static Expr *inline_function(Oid funcid, Oid result_type, Oid result_collid,
--- 135,150 ----
  				Oid input_collid, List *args,
  				HeapTuple func_tuple,
  				eval_const_expressions_context *context);
+ static int	expression_params_used(Node *node);
  static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
  							 int *usecounts);
  static Node *substitute_actual_parameters_mutator(Node *node,
  							  substitute_actual_parameters_context *context);
+ static Node *adjust_expression_params(Node *expr, int offset);
+ static Node *adjust_expression_params_mutator(Node *node, int *offset);
+ static Node *replace_expression_param(Node *expr, Node *replacement);
+ static Node *replace_expression_param_mutator(Node *node,
+ 								 replace_expression_param_context *context);
  static void sql_inline_error_callback(void *arg);
  static Expr *evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
  			  Oid result_collation);
***************
*** 2104,2110 **** eval_const_expressions(PlannerInfo *root, Node *node)
  		context.glob = NULL;
  	}
  	context.active_fns = NIL;	/* nothing being recursively simplified */
- 	context.case_val = NULL;	/* no CASE being examined */
  	context.estimate = false;	/* safe transformations only */
  	return eval_const_expressions_mutator(node, &context);
  }
--- 2116,2121 ----
***************
*** 2135,2141 **** estimate_expression_value(PlannerInfo *root, Node *node)
  	/* we do not need to mark the plan as depending on inlined functions */
  	context.glob = NULL;
  	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);
  }
--- 2146,2151 ----
***************
*** 2700,2706 **** eval_const_expressions_mutator(Node *node,
  		 * CASE to the default result (ELSE result).
  		 *
  		 * If we have a simple-form CASE with constant test expression,
! 		 * we substitute the constant value for contained CaseTestExpr
  		 * placeholder nodes, so that we have the opportunity to reduce
  		 * constant test conditions.  For example this allows
  		 *		CASE 0 WHEN 0 THEN 1 ELSE 1/0 END
--- 2710,2716 ----
  		 * CASE to the default result (ELSE result).
  		 *
  		 * If we have a simple-form CASE with constant test expression,
! 		 * we substitute the constant value for contained ExpressionParam
  		 * placeholder nodes, so that we have the opportunity to reduce
  		 * constant test conditions.  For example this allows
  		 *		CASE 0 WHEN 0 THEN 1 ELSE 1/0 END
***************
*** 2709,2750 **** eval_const_expressions_mutator(Node *node,
  		 * include it in the resulting CASE; for example
  		 *		CASE 0 WHEN x THEN y ELSE z END
  		 * is transformed by the parser to
! 		 *		CASE 0 WHEN CaseTestExpr = x THEN y ELSE z END
  		 * which we can simplify to
  		 *		CASE WHEN 0 = x THEN y ELSE z END
  		 * It is not necessary for the executor to evaluate the "arg"
  		 * expression when executing the CASE, since any contained
! 		 * CaseTestExprs that might have referred to it will have been
  		 * replaced by the constant.
  		 *----------
  		 */
  		CaseExpr   *caseexpr = (CaseExpr *) node;
  		CaseExpr   *newcase;
- 		Node	   *save_case_val;
  		Node	   *newarg;
  		List	   *newargs;
  		bool		const_true_cond;
  		Node	   *defresult = NULL;
  		ListCell   *arg;
  
  		/* Simplify the test expression, if any */
  		newarg = eval_const_expressions_mutator((Node *) caseexpr->arg,
  												context);
  
! 		/* Set up for contained CaseTestExpr nodes */
! 		save_case_val = context->case_val;
! 		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;
  		const_true_cond = false;
! 		foreach(arg, caseexpr->args)
  		{
  			CaseWhen   *oldcasewhen = (CaseWhen *) lfirst(arg);
  			Node	   *casecond;
--- 2719,2776 ----
  		 * include it in the resulting CASE; for example
  		 *		CASE 0 WHEN x THEN y ELSE z END
  		 * is transformed by the parser to
! 		 *		CASE 0 WHEN ExpressionParam = x THEN y ELSE z END
  		 * which we can simplify to
  		 *		CASE WHEN 0 = x THEN y ELSE z END
  		 * It is not necessary for the executor to evaluate the "arg"
  		 * expression when executing the CASE, since any contained
! 		 * ExpressionParams that might have referred to it will have been
  		 * replaced by the constant.
  		 *----------
  		 */
  		CaseExpr   *caseexpr = (CaseExpr *) node;
  		CaseExpr   *newcase;
  		Node	   *newarg;
  		List	   *newargs;
  		bool		const_true_cond;
  		Node	   *defresult = NULL;
  		ListCell   *arg;
+ 		List	   *args;
  
  		/* Simplify the test expression, if any */
  		newarg = eval_const_expressions_mutator((Node *) caseexpr->arg,
  												context);
  
! 		/*
! 		 * Set up for contained ExpressionParam nodes. If the case value is
! 		 * a constant, we can simply replace the ExpressionParam nodes with the
! 		 * constant. Otherwise, create an expression 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.
! 		 *
! 		 * Note: These conditions must match those in expression_params_used()!
! 		 */
! 		args = caseexpr->args;
! 		if (newarg)
  		{
! 			if (IsA(newarg, Const))
! 			{
! 				/*
! 				 * Replace any ExpressionParams referring to this with the
! 				 * constant. This also adjusts the levelsup fields of any
! 				 * ExpressionParams referring to values above this level.
! 				 */
! 				args = (List *) replace_expression_param((Node *) args, newarg);
! 				newarg = NULL;		/* not needed anymore */
! 			}
  		}
  
  		/* Simplify the WHEN clauses */
  		newargs = NIL;
  		const_true_cond = false;
! 		foreach(arg, args)
  		{
  			CaseWhen   *oldcasewhen = (CaseWhen *) lfirst(arg);
  			Node	   *casecond;
***************
*** 2803,2810 **** eval_const_expressions_mutator(Node *node,
  				eval_const_expressions_mutator((Node *) caseexpr->defresult,
  											   context);
  
- 		context->case_val = save_case_val;
- 
  		/* If no non-FALSE alternatives, CASE reduces to the default result */
  		if (newargs == NIL)
  			return defresult;
--- 2829,2834 ----
***************
*** 2818,2835 **** eval_const_expressions_mutator(Node *node,
  		newcase->location = caseexpr->location;
  		return (Node *) newcase;
  	}
- 	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))
  	{
  		ArrayExpr  *arrayexpr = (ArrayExpr *) node;
--- 2842,2847 ----
***************
*** 4139,4144 **** fail:
--- 4151,4287 ----
  }
  
  /*
+  * Returns the number of expression params pushed to the stack by this node.
+  */
+ static int
+ expression_params_used(Node *node)
+ {
+ 	/*
+ 	 * "CASE x WHEN ..." constructs push the CASE-value x to an expression
+ 	 * param.
+ 	 */
+ 	if (IsA(node, CaseExpr))
+ 	{
+ 		CaseExpr   *caseexpr = (CaseExpr *) node;
+ 		/*
+ 		 * Note: these conditions must match those in
+ 		 * eval_const_expressions_mutator()!
+ 		 */
+ 		if (caseexpr->arg && !IsA(caseexpr->arg, Const))
+ 			return 1;
+ 	}
+ 	if (IsA(node, FieldStore))
+ 	{
+ 		FieldStore   *fstore = (FieldStore *) node;
+ 		if (fstore->useparam)
+ 			return 1;
+ 	}
+ 	if (IsA(node, ArrayRef))
+ 	{
+ 		ArrayRef   *aref = (ArrayRef *) node;
+ 		if (aref->refexpr)
+ 			return 1;
+ 	}
+ 	/*
+ 	 * XXX
+ 	 *
+ 	 * IN will push one param, the left-hand value. BETWEEN will also push one,
+ 	 * BETWEEN SYMMETRIC will push three values in the worst case.
+ 	 */
+ 	return 0;
+ }
+ 
+ /*
+  *
+  */
+ static Node *
+ adjust_expression_params(Node *node, int offset)
+ {
+ 	if (offset == 0)
+ 		return node;
+ 
+ 	return adjust_expression_params_mutator(node, &offset);
+ }
+ 
+ static Node *
+ adjust_expression_params_mutator(Node *node, int *offset)
+ {
+ 	if (node == NULL)
+ 		return NULL;
+ 	if (IsA(node, ExpressionParam))
+ 	{
+ 		ExpressionParam *eparam = (ExpressionParam *) copyObject(node);
+ 		eparam->levelsup += (*offset);
+ 		return (Node *) eparam;
+ 	}
+ 	return expression_tree_mutator(node, adjust_expression_params_mutator,
+ 								   (void *) offset);
+ }
+ 
+ /*
+  * Replaces any ExpressionParams (with eparamid == 0) in 'node' with the
+  * given replacement node.
+  *
+  * The difficulty is in tracking which ExpressionParams point to the value
+  * we're trying to replace. As we drill into a CaseExpr, for example, the
+  * "current" depth we're at increases. Within one such a node the
+  * ExpressionParams we need to replace will have levelsup == 1.
+  */
+ static Node *
+ replace_expression_param(Node *node, Node *replacement)
+ {
+ 	replace_expression_param_context context;
+ 
+ 	/* we are replacing ExpressionParams with levelsup 0 initially */
+ 	context.replacement_level = 0;
+ 	context.replacement = replacement;
+ 	return replace_expression_param_mutator(node, &context);
+ }
+ 
+ static Node *
+ replace_expression_param_mutator(Node *node, replace_expression_param_context *context)
+ {
+ 	int			num_expr_params;
+ 
+ 	if (node == NULL)
+ 		return NULL;
+ 	if (IsA(node, ExpressionParam))
+ 	{
+ 		ExpressionParam *eparam = (ExpressionParam *) copyObject(node);
+ 
+ 		/*
+ 		 * If this param points to the param that we're replacing, return
+ 		 * the replacement. If it points to something above it, shift the
+ 		 * reference down as we're removing the layer from the middle. If
+ 		 * it points to something below it, leave it alone.
+ 		 */
+ 		if (eparam->levelsup == context->replacement_level)
+ 			return context->replacement; /* XXX copy? */
+ 		else if (eparam->levelsup > context->replacement_level)
+ 		{
+ 			ExpressionParam *neweparam = (ExpressionParam *) copyObject(eparam);
+ 			neweparam->levelsup--;
+ 			return (Node *) neweparam;
+ 		}
+ 		else
+ 			return (Node *) eparam;
+ 	}
+ 
+ 	num_expr_params = expression_params_used(node);
+ 	if (num_expr_params > 0)
+ 	{
+ 		context->replacement_level += num_expr_params;
+ 		return expression_tree_mutator(node, replace_expression_param_mutator,
+ 									   (void *) context);
+ 		context->replacement_level -= num_expr_params;
+ 	}
+ 	else
+ 		return expression_tree_mutator(node, replace_expression_param_mutator,
+ 									   (void *) context);
+ }
+ 
+ 
+ /*
   * Replace Param nodes by appropriate actual parameters
   */
  static Node *
***************
*** 4150,4155 **** substitute_actual_parameters(Node *expr, int nargs, List *args,
--- 4293,4299 ----
  	context.nargs = nargs;
  	context.args = args;
  	context.usecounts = usecounts;
+ 	context.expr_param_depth = 0;
  
  	return substitute_actual_parameters_mutator(expr, &context);
  }
***************
*** 4158,4168 **** static Node *
--- 4302,4316 ----
  substitute_actual_parameters_mutator(Node *node,
  							   substitute_actual_parameters_context *context)
  {
+ 	int			num_expr_params;
+ 
  	if (node == NULL)
  		return NULL;
+ 
  	if (IsA(node, Param))
  	{
  		Param	   *param = (Param *) node;
+ 		Node	   *arg;
  
  		if (param->paramkind != PARAM_EXTERN)
  			elog(ERROR, "unexpected paramkind: %d", (int) param->paramkind);
***************
*** 4172,4183 **** substitute_actual_parameters_mutator(Node *node,
  		/* Count usage of parameter */
  		context->usecounts[param->paramid - 1]++;
  
! 		/* Select the appropriate actual arg and replace the Param with it */
! 		/* We don't need to copy at this time (it'll get done later) */
! 		return list_nth(context->args, param->paramid - 1);
  	}
! 	return expression_tree_mutator(node, substitute_actual_parameters_mutator,
! 								   (void *) context);
  }
  
  /*
--- 4320,4349 ----
  		/* Count usage of parameter */
  		context->usecounts[param->paramid - 1]++;
  
! 		/*
! 		 * Select the appropriate actual arg and replace the Param with it.
! 		 * We don't need to copy at this time (it'll get done later).
! 		 * However, if we're within a node that sets an ExpressionParam, we
! 		 * need to adjust any ExpressionParam references to outer expressions
! 		 * accordingly.
! 		 */
! 		arg = list_nth(context->args, param->paramid - 1);
! 		if (context->expr_param_depth > 0)
! 			arg = adjust_expression_params(arg, context->expr_param_depth);
! 		return arg;
! 	}
! 
! 	num_expr_params = expression_params_used(node);
! 	if (num_expr_params > 0)
! 	{
! 		context->expr_param_depth += num_expr_params;
! 		return expression_tree_mutator(node, substitute_actual_parameters_mutator,
! 									   (void *) context);
! 		context->expr_param_depth -= num_expr_params;
  	}
! 	else
! 		return expression_tree_mutator(node, substitute_actual_parameters_mutator,
! 									   (void *) context);
  }
  
  /*
*** a/src/backend/parser/parse_collate.c
--- b/src/backend/parser/parse_collate.c
***************
*** 598,604 **** assign_collations_walker(Node *node, assign_collations_context *context)
  		case T_Const:
  		case T_Param:
  		case T_CoerceToDomainValue:
! 		case T_CaseTestExpr:
  		case T_SetToDefault:
  		case T_CurrentOfExpr:
  
--- 598,604 ----
  		case T_Const:
  		case T_Param:
  		case T_CoerceToDomainValue:
! 		case T_ExpressionParam:
  		case T_SetToDefault:
  		case T_CurrentOfExpr:
  
*** a/src/backend/parser/parse_expr.c
--- b/src/backend/parser/parse_expr.c
***************
*** 321,327 **** transformExpr(ParseState *pstate, Node *expr)
  		case T_ArrayCoerceExpr:
  		case T_ConvertRowtypeExpr:
  		case T_CollateExpr:
! 		case T_CaseTestExpr:
  		case T_ArrayExpr:
  		case T_CoerceToDomain:
  		case T_CoerceToDomainValue:
--- 321,327 ----
  		case T_ArrayCoerceExpr:
  		case T_ConvertRowtypeExpr:
  		case T_CollateExpr:
! 		case T_ExpressionParam:
  		case T_ArrayExpr:
  		case T_CoerceToDomain:
  		case T_CoerceToDomainValue:
***************
*** 1253,1259 **** transformCaseExpr(ParseState *pstate, CaseExpr *c)
  {
  	CaseExpr   *newc;
  	Node	   *arg;
! 	CaseTestExpr *placeholder;
  	List	   *newargs;
  	List	   *resultexprs;
  	ListCell   *l;
--- 1253,1259 ----
  {
  	CaseExpr   *newc;
  	Node	   *arg;
! 	ExpressionParam *placeholder;
  	List	   *newargs;
  	List	   *resultexprs;
  	ListCell   *l;
***************
*** 1286,1296 **** transformCaseExpr(ParseState *pstate, CaseExpr *c)
  		 * Run collation assignment on the test expression so that we know
  		 * what collation to mark the placeholder with.  In principle we could
  		 * leave it to parse_collate.c to do that later, but propagating the
! 		 * result to the CaseTestExpr would be unnecessarily complicated.
  		 */
  		assign_expr_collations(pstate, arg);
  
! 		placeholder = makeNode(CaseTestExpr);
  		placeholder->typeId = exprType(arg);
  		placeholder->typeMod = exprTypmod(arg);
  		placeholder->collation = exprCollation(arg);
--- 1286,1297 ----
  		 * Run collation assignment on the test expression so that we know
  		 * what collation to mark the placeholder with.  In principle we could
  		 * leave it to parse_collate.c to do that later, but propagating the
! 		 * result to the ExpressionParam would be unnecessarily complicated.
  		 */
  		assign_expr_collations(pstate, arg);
  
! 		placeholder = makeNode(ExpressionParam);
! 		placeholder->levelsup = 0;
  		placeholder->typeId = exprType(arg);
  		placeholder->typeMod = exprTypmod(arg);
  		placeholder->collation = exprCollation(arg);
*** a/src/backend/parser/parse_target.c
--- b/src/backend/parser/parse_target.c
***************
*** 583,598 **** transformAssignmentIndirection(ParseState *pstate,
  	List	   *subscripts = NIL;
  	bool		isSlice = false;
  	ListCell   *i;
  
  	if (indirection && !basenode)
  	{
! 		/* Set up a substitution.  We reuse CaseTestExpr for this. */
! 		CaseTestExpr *ctest = makeNode(CaseTestExpr);
! 
! 		ctest->typeId = targetTypeId;
! 		ctest->typeMod = targetTypMod;
! 		ctest->collation = targetCollation;
! 		basenode = (Node *) ctest;
  	}
  
  	/*
--- 583,601 ----
  	List	   *subscripts = NIL;
  	bool		isSlice = false;
  	ListCell   *i;
+ 	bool		useparam = false;
  
  	if (indirection && !basenode)
  	{
! 		/* Set up a substitution. */
! 		ExpressionParam *eparam = makeNode(ExpressionParam);
! 
! 		eparam->levelsup = 0;
! 		eparam->typeId = targetTypeId;
! 		eparam->typeMod = targetTypMod;
! 		eparam->collation = targetCollation;
! 		basenode = (Node *) eparam;
! 		useparam = true;
  	}
  
  	/*
***************
*** 691,696 **** transformAssignmentIndirection(ParseState *pstate,
--- 694,704 ----
  			fstore->arg = (Expr *) basenode;
  			fstore->newvals = list_make1(rhs);
  			fstore->fieldnums = list_make1_int(attnum);
+ 			/*
+ 			 * if there's any more recursion, need to store the old value
+ 			 * of the field in an expression param at execution time
+ 			 */
+ 			fstore->useparam = (lnext(i) != NULL);
  			fstore->resulttype = targetTypeId;
  
  			return (Node *) fstore;
***************
*** 765,770 **** transformAssignmentSubscripts(ParseState *pstate,
--- 773,779 ----
  							  Node *rhs,
  							  int location)
  {
+ 	ArrayRef   *aref;
  	Node	   *result;
  	Oid			arrayType;
  	int32		arrayTypMod;
***************
*** 805,817 **** transformAssignmentSubscripts(ParseState *pstate,
  										 location);
  
  	/* process subscripts */
! 	result = (Node *) transformArraySubscripts(pstate,
! 											   basenode,
! 											   arrayType,
! 											   elementTypeId,
! 											   arrayTypMod,
! 											   subscripts,
! 											   rhs);
  
  	/* If target was a domain over array, need to coerce up to the domain */
  	if (arrayType != targetTypeId)
--- 814,854 ----
  										 location);
  
  	/* process subscripts */
! 	aref = transformArraySubscripts(pstate,
! 									  basenode,
! 									  arrayType,
! 									  elementTypeId,
! 									  arrayTypMod,
! 									  subscripts,
! 									  rhs);
! 	/*
! 	 * We might have a nested-assignment situation, in which the rhs 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 at runtime and pass it down
! 	 * via an expression param.
! 	 *
! 	 * Since fetching the old element might be a nontrivial expense, do it
! 	 * only if the argument appears to actually need it. We set refuseparam
! 	 * if that is needed.
! 	 */
! 	if (IsA(rhs, FieldStore))
! 	{
! 		FieldStore *fstore = (FieldStore *) rhs;
! 
! 		if (fstore->arg && IsA(fstore->arg, ExpressionParam) &&
! 			((ExpressionParam *) fstore->arg)->levelsup == 0)
! 			aref->refuseparam = true;
! 	}
! 	else if (IsA(rhs, ArrayRef))
! 	{
! 		ArrayRef   *arrayRef = (ArrayRef *) rhs;
! 
! 		if (arrayRef->refexpr && IsA(arrayRef->refexpr, ExpressionParam) &&
! 			((ExpressionParam *) arrayRef->refexpr)->levelsup == 0)
! 			aref->refuseparam = true;
! 	}
! 	result = (Node *) aref;
  
  	/* If target was a domain over array, need to coerce up to the domain */
  	if (arrayType != targetTypeId)
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
***************
*** 4783,4796 **** get_rule_expr(Node *node, deparse_context *context,
  				bool		need_parens;
  
  				/*
! 				 * If the argument is a CaseTestExpr, we must be inside a
  				 * FieldStore, ie, we are assigning to an element of an array
  				 * within a composite column.  Since we already punted on
  				 * displaying the FieldStore's target information, just punt
  				 * here too, and display only the assignment source
  				 * expression.
  				 */
! 				if (IsA(aref->refexpr, CaseTestExpr))
  				{
  					Assert(aref->refassgnexpr);
  					get_rule_expr((Node *) aref->refassgnexpr,
--- 4783,4797 ----
  				bool		need_parens;
  
  				/*
! 				 * If the argument is an ExpressionParam, we must be inside a
  				 * FieldStore, ie, we are assigning to an element of an array
  				 * within a composite column.  Since we already punted on
  				 * displaying the FieldStore's target information, just punt
  				 * here too, and display only the assignment source
  				 * expression.
  				 */
! 				if (IsA(aref->refexpr, ExpressionParam) &&
! 					((ExpressionParam *) aref->refexpr)->levelsup == 0)
  				{
  					Assert(aref->refassgnexpr);
  					get_rule_expr((Node *) aref->refassgnexpr,
***************
*** 5191,5198 **** get_rule_expr(Node *node, deparse_context *context,
  					{
  						/*
  						 * The parser should have produced WHEN clauses of
! 						 * the form "CaseTestExpr = RHS", possibly with an
! 						 * implicit coercion inserted above the CaseTestExpr.
  						 * For accurate decompilation of rules it's essential
  						 * that we show just the RHS.  However in an
  						 * expression that's been through the optimizer, the
--- 5192,5199 ----
  					{
  						/*
  						 * The parser should have produced WHEN clauses of
! 						 * the form "ExpressionParam[0] = RHS", possibly with an
! 						 * implicit coercion inserted above the ExpressionParam.
  						 * For accurate decompilation of rules it's essential
  						 * that we show just the RHS.  However in an
  						 * expression that's been through the optimizer, the
***************
*** 5204,5214 **** get_rule_expr(Node *node, deparse_context *context,
  						if (IsA(w, OpExpr))
  						{
  							List	   *args = ((OpExpr *) w)->args;
  
! 							if (list_length(args) == 2 &&
! 								IsA(strip_implicit_coercions(linitial(args)),
! 									CaseTestExpr))
! 								w = (Node *) lsecond(args);
  						}
  					}
  
--- 5205,5218 ----
  						if (IsA(w, OpExpr))
  						{
  							List	   *args = ((OpExpr *) w)->args;
+ 							if (list_length(args) == 2)
+ 							{
+ 								Node *leftarg = strip_implicit_coercions(linitial(args));
  
! 								if (IsA(leftarg, ExpressionParam) &&
! 									((ExpressionParam *) leftarg)->levelsup == 0)
! 									w = (Node *) lsecond(args);
! 							}
  						}
  					}
  
***************
*** 5232,5238 **** get_rule_expr(Node *node, deparse_context *context,
  			}
  			break;
  
! 		case T_CaseTestExpr:
  			{
  				/*
  				 * Normally we should never get here, since for expressions
--- 5236,5242 ----
  			}
  			break;
  
! 		case T_ExpressionParam:
  			{
  				/*
  				 * Normally we should never get here, since for expressions
***************
*** 5241,5247 **** get_rule_expr(Node *node, deparse_context *context,
  				 * be unable to avoid that (see comments for CaseExpr).  If we
  				 * do see one, print it as CASE_TEST_EXPR.
  				 */
! 				appendStringInfo(buf, "CASE_TEST_EXPR");
  			}
  			break;
  
--- 5245,5252 ----
  				 * be unable to avoid that (see comments for CaseExpr).  If we
  				 * do see one, print it as CASE_TEST_EXPR.
  				 */
! 				appendStringInfo(buf, "EXPR_PARAM[%d]",
! 								 ((ExpressionParam *) node)->levelsup);
  			}
  			break;
  
*** a/src/backend/utils/adt/selfuncs.c
--- b/src/backend/utils/adt/selfuncs.c
***************
*** 1836,1842 **** scalararraysel(PlannerInfo *root,
  	}
  	else
  	{
! 		CaseTestExpr *dummyexpr;
  		List	   *args;
  		Selectivity s2;
  		int			i;
--- 1836,1842 ----
  	}
  	else
  	{
! 		ExpressionParam *dummyexpr;
  		List	   *args;
  		Selectivity s2;
  		int			i;
***************
*** 1844,1852 **** scalararraysel(PlannerInfo *root,
  		/*
  		 * We need a dummy rightop to pass to the operator selectivity
  		 * routine.  It can be pretty much anything that doesn't look like a
! 		 * constant; CaseTestExpr is a convenient choice.
  		 */
! 		dummyexpr = makeNode(CaseTestExpr);
  		dummyexpr->typeId = nominal_element_type;
  		dummyexpr->typeMod = -1;
  		dummyexpr->collation = clause->inputcollid;
--- 1844,1853 ----
  		/*
  		 * We need a dummy rightop to pass to the operator selectivity
  		 * routine.  It can be pretty much anything that doesn't look like a
! 		 * constant; ExpressionParam is a convenient choice.
  		 */
! 		dummyexpr = makeNode(ExpressionParam);
! 		dummyexpr->levelsup = 0;
  		dummyexpr->typeId = nominal_element_type;
  		dummyexpr->typeMod = -1;
  		dummyexpr->collation = clause->inputcollid;
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 125,130 **** typedef struct ExprContext
--- 125,132 ----
  	ParamExecData *ecxt_param_exec_vals;		/* for PARAM_EXEC params */
  	ParamListInfo ecxt_param_list_info; /* for other param types */
  
+ 	List	   *expr_params;	/* stack of ParamExecDatas */
+ 
  	/*
  	 * Values to substitute for Aggref nodes in the expressions of an Agg
  	 * node, or for WindowFunc nodes within a WindowAgg node.
***************
*** 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;
--- 134,139 ----
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
***************
*** 152,158 **** typedef enum NodeTag
  	T_CollateExpr,
  	T_CaseExpr,
  	T_CaseWhen,
! 	T_CaseTestExpr,
  	T_ArrayExpr,
  	T_RowExpr,
  	T_RowCompareExpr,
--- 152,158 ----
  	T_CollateExpr,
  	T_CaseExpr,
  	T_CaseWhen,
! 	T_ExpressionParam,
  	T_ArrayExpr,
  	T_RowExpr,
  	T_RowCompareExpr,
*** a/src/include/nodes/primnodes.h
--- b/src/include/nodes/primnodes.h
***************
*** 212,217 **** typedef struct Param
--- 212,239 ----
  } Param;
  
  /*
+  * ExpressionParam
+  *
+  * Expression params are similar to Params, but are only used within a single
+  * expression. An enclosing Expr node can set a param, and an inner Expr
+  * can reference it using an ExpressionParam node. Unlike normal Params,
+  * expression param ids are not globally unique. Instead, they form a stack.
+  * An ExpressionParam with eparamid == 0 references the param value at the
+  * top of the stack, eparamid == 1 the one below it, and so forth. The syntax
+  * of the expressions that use this only allow referencing the topmost param,
+  * but SQL function inlining can rearrange things.
+  */
+ typedef struct ExpressionParam
+ {
+ 	Expr		xpr;
+ 	int			levelsup;		/* stack level this refers to */
+ 	Oid			typeId;			/* pg_type OID of parameter's datatype */
+ 	int32		typeMod;		/* typmod value, if known */
+ 	Oid			collation;		/* OID of collation, or InvalidOid if none */
+ 	int			location;		/* token location, or -1 if unknown */
+ } ExpressionParam;
+ 
+ /*
   * Aggref
   *
   * The aggregate's args list is a targetlist, ie, a list of TargetEntry nodes
***************
*** 295,300 **** typedef struct ArrayRef
--- 317,324 ----
  								 * value */
  	Expr	   *refassgnexpr;	/* expression for the source value, or NULL if
  								 * fetch */
+ 	bool		refuseparam;	/* need an exec param to hold old value during
+ 								 * refassgnexpr execution? */
  } ArrayRef;
  
  /*
***************
*** 639,644 **** typedef struct FieldStore
--- 663,669 ----
  	Expr	   *arg;			/* input tuple value */
  	List	   *newvals;		/* new value(s) for field(s) */
  	List	   *fieldnums;		/* integer list of field attnums */
+ 	bool		useparam;		/* need an expr param to hold old values? */
  	Oid			resulttype;		/* type of result (same as type of arg) */
  	/* Like RowExpr, we deliberately omit a typmod and collation here */
  } FieldStore;
***************
*** 761,768 **** typedef struct CollateExpr
   * In the raw grammar output for the second form, the condition expressions
   * of the WHEN clauses are just the comparison values.	Parse analysis
   * converts these to valid boolean expressions of the form
!  *		CaseTestExpr '=' compexpr
!  * where the CaseTestExpr node is a placeholder that emits the correct
   * value at runtime.  This structure is used so that the testexpr need be
   * evaluated only once.  Note that after parse analysis, the condition
   * expressions always yield boolean.
--- 786,793 ----
   * In the raw grammar output for the second form, the condition expressions
   * of the WHEN clauses are just the comparison values.	Parse analysis
   * converts these to valid boolean expressions of the form
!  *		ExpressionParam[0] '=' compexpr
!  * where the ExpressionParam node is a placeholder that emits the correct
   * value at runtime.  This structure is used so that the testexpr need be
   * evaluated only once.  Note that after parse analysis, the condition
   * expressions always yield boolean.
***************
*** 794,815 **** typedef struct CaseWhen
  } CaseWhen;
  
  /*
-  * Placeholder node for the test value to be processed by a CASE expression.
-  * This is effectively like a Param, but can be implemented more simply
-  * since we need only one replacement value at a time.
-  *
-  * We also use this in nested UPDATE expressions.
-  * See transformAssignmentIndirection().
-  */
- typedef struct CaseTestExpr
- {
- 	Expr		xpr;
- 	Oid			typeId;			/* type for substituted value */
- 	int32		typeMod;		/* typemod for substituted value */
- 	Oid			collation;		/* collation for the substituted value */
- } CaseTestExpr;
- 
- /*
   * ArrayExpr - an ARRAY[] expression
   *
   * Note: if multidims is false, the constituent expressions all yield the
--- 819,824 ----
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
***************
*** 5459,5465 **** exec_simple_check_node(Node *node)
  				return TRUE;
  			}
  
! 		case T_CaseTestExpr:
  			return TRUE;
  
  		case T_ArrayExpr:
--- 5459,5465 ----
  				return TRUE;
  			}
  
! 		case T_ExpressionParam:
  			return TRUE;
  
  		case T_ArrayExpr:
