replace-casetestexpr-with-expressionparams-2.patch
text/x-diff
Filename: replace-casetestexpr-with-expressionparams-2.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/execQual.c | 71 | 1 |
| src/backend/executor/execUtils.c | 0 | 0 |
| src/backend/nodes/copyfuncs.c | 10 | 0 |
| src/backend/nodes/equalfuncs.c | 7 | 0 |
| src/backend/nodes/nodeFuncs.c | 8 | 0 |
| src/backend/nodes/outfuncs.c | 8 | 0 |
| src/backend/nodes/readfuncs.c | 10 | 0 |
| src/backend/optimizer/util/clauses.c | 200 | 0 |
| src/backend/parser/parse_collate.c | 1 | 0 |
| src/backend/parser/parse_expr.c | 5 | 0 |
| src/backend/parser/parse_target.c | 51 | 0 |
| src/backend/utils/adt/ruleutils.c | 15 | 0 |
| src/backend/utils/adt/selfuncs.c | 4 | 0 |
| src/include/nodes/execnodes.h | 2 | 0 |
| src/include/nodes/nodes.h | 1 | 0 |
| src/include/nodes/primnodes.h | 27 | 0 |
| src/pl/plpgsql/src/pl_exec.c | 1 | 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 ----
***************
*** 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: