replace-casetestexpr-with-param-1.patch
text/x-diff
Filename: replace-casetestexpr-with-param-1.patch
Type: text/x-diff
Part: 0
Message:
Re: Nested CASE-WHEN scoping
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
| File | + | − |
|---|---|---|
| src/backend/executor/execMain.c | 2 | 0 |
| src/backend/executor/execQual.c | 61 | 1 |
| src/backend/executor/execUtils.c | 2 | 0 |
| src/backend/nodes/copyfuncs.c | 3 | 0 |
| src/backend/nodes/equalfuncs.c | 3 | 0 |
| src/backend/nodes/outfuncs.c | 3 | 0 |
| src/backend/nodes/readfuncs.c | 3 | 0 |
| src/backend/optimizer/plan/planner.c | 2 | 0 |
| src/backend/optimizer/util/clauses.c | 182 | 0 |
| src/include/nodes/execnodes.h | 3 | 0 |
| src/include/nodes/primnodes.h | 7 | 0 |
| src/pl/plpgsql/src/pl_exec.c | 0 | 0 |
*** 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),
! ¶mid);
/* 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,
! ¶mid);
! 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,
! ¶mid);
! }
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 ----