On 30.05.2011 17:21, Tom Lane wrote:
Heikki Linnakangas<heikki.linnakan...@enterprisedb.com>  writes:
I think we can work around both of those by just saving and restoring
the value of each Param that we set while evaluating an expression,

Huh?  That's a waste of time and effort.  Just make sure that each such
spot has its own Param number.  That's why I'm telling you to do it in
the planner, not the parser --- it is easy to assign globally-unique-
across-the-plan numbers at plan time, in fact we do it already.

Yeah, I got that part. What I'm saying is that it's not that easy, because of the two issues, domain constraints and index expressions, that I mentioned. Here's a WIP patch, but those two issues have not been addressed yet. I'm sure those are not insurmountable problems, I'm just trying to figure out the best way to surmount them:

For domain constraints, ExecInitExpr could assign globally-unique param numbers if it knew how many params are in use. That's trivial for expressions in plans, as you have access to the EState via the PlanState, and EState can include the number of params assigned. For a stand-alone expression, we don't have that. There is no global information whatsoever for stand-alone expressions, ExecInitExpr only sees the current node it's dealing with. Perhaps we need to add the concept of a global plan

For index expressions, we could use a function similar to ChangeVarNodes(), that shifts all the paramids in the already-planned expression, preparing it for inclusion within the enclosing plan. I'm a bit worried that that might screw up the logic used to compare if an expression matches the index expression, though; the param ids in the two expressions won't match.

--
  Heikki Linnakangas
  EnterpriseDB   http://www.enterprisedb.com
*** 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 ----
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to