redesign-plpgsql-parameter-passing-1.patch
text/x-diff
Filename: redesign-plpgsql-parameter-passing-1.patch
Type: text/x-diff
Part: 0
Patch
Format: unified
| File | + | − |
|---|---|---|
| src/backend/commands/prepare.c | 0 | 0 |
| src/backend/executor/execCurrent.c | 0 | 0 |
| src/backend/executor/execExpr.c | 0 | 0 |
| src/backend/executor/execExprInterp.c | 0 | 0 |
| src/backend/executor/functions.c | 0 | 0 |
| src/backend/executor/spi.c | 0 | 0 |
| src/backend/nodes/params.c | 0 | 0 |
| src/backend/optimizer/util/clauses.c | 0 | 0 |
| src/backend/tcop/postgres.c | 0 | 0 |
| src/include/executor/execExpr.h | 0 | 0 |
| src/include/executor/executor.h | 0 | 0 |
| src/include/nodes/execnodes.h | 0 | 0 |
| src/include/nodes/params.h | 0 | 0 |
| src/pl/plpgsql/src/pl_comp.c | 0 | 0 |
| src/pl/plpgsql/src/pl_exec.c | 0 | 0 |
| src/pl/plpgsql/src/plpgsql.h | 0 | 0 |
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index be7222f..6e90912 100644
*** a/src/backend/commands/prepare.c
--- b/src/backend/commands/prepare.c
*************** EvaluateParams(PreparedStatement *pstmt,
*** 399,408 ****
/* we have static list of params, so no hooks needed */
paramLI->paramFetch = NULL;
paramLI->paramFetchArg = NULL;
paramLI->parserSetup = NULL;
paramLI->parserSetupArg = NULL;
paramLI->numParams = num_params;
- paramLI->paramMask = NULL;
i = 0;
foreach(l, exprstates)
--- 399,409 ----
/* we have static list of params, so no hooks needed */
paramLI->paramFetch = NULL;
paramLI->paramFetchArg = NULL;
+ paramLI->paramCompile = NULL;
+ paramLI->paramCompileArg = NULL;
paramLI->parserSetup = NULL;
paramLI->parserSetupArg = NULL;
paramLI->numParams = num_params;
i = 0;
foreach(l, exprstates)
diff --git a/src/backend/executor/execCurrent.c b/src/backend/executor/execCurrent.c
index a3e962e..eaeb3a2 100644
*** a/src/backend/executor/execCurrent.c
--- b/src/backend/executor/execCurrent.c
*************** fetch_cursor_param_value(ExprContext *ec
*** 216,226 ****
if (paramInfo &&
paramId > 0 && paramId <= paramInfo->numParams)
{
! ParamExternData *prm = ¶mInfo->params[paramId - 1];
/* give hook a chance in case parameter is dynamic */
! if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
! paramInfo->paramFetch(paramInfo, paramId);
if (OidIsValid(prm->ptype) && !prm->isnull)
{
--- 216,229 ----
if (paramInfo &&
paramId > 0 && paramId <= paramInfo->numParams)
{
! ParamExternData *prm;
! ParamExternData prmdata;
/* give hook a chance in case parameter is dynamic */
! if (paramInfo->paramFetch != NULL)
! prm = paramInfo->paramFetch(paramInfo, paramId, false, &prmdata);
! else
! prm = ¶mInfo->params[paramId - 1];
if (OidIsValid(prm->ptype) && !prm->isnull)
{
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index e083961..94ebe76 100644
*** a/src/backend/executor/execExpr.c
--- b/src/backend/executor/execExpr.c
*************** typedef struct LastAttnumInfo
*** 55,76 ****
} LastAttnumInfo;
static void ExecReadyExpr(ExprState *state);
! static void ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
Datum *resv, bool *resnull);
- static void ExprEvalPushStep(ExprState *es, const ExprEvalStep *s);
static void ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args,
! Oid funcid, Oid inputcollid, PlanState *parent,
ExprState *state);
static void ExecInitExprSlots(ExprState *state, Node *node);
static bool get_last_attnums_walker(Node *node, LastAttnumInfo *info);
static void ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable,
! PlanState *parent);
static void ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref,
! PlanState *parent, ExprState *state,
Datum *resv, bool *resnull);
static bool isAssignmentIndirectionExpr(Expr *expr);
static void ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
! PlanState *parent, ExprState *state,
Datum *resv, bool *resnull);
--- 55,75 ----
} LastAttnumInfo;
static void ExecReadyExpr(ExprState *state);
! static void ExecInitExprRec(Expr *node, ExprState *state,
Datum *resv, bool *resnull);
static void ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args,
! Oid funcid, Oid inputcollid,
ExprState *state);
static void ExecInitExprSlots(ExprState *state, Node *node);
static bool get_last_attnums_walker(Node *node, LastAttnumInfo *info);
static void ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable,
! ExprState *state);
static void ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref,
! ExprState *state,
Datum *resv, bool *resnull);
static bool isAssignmentIndirectionExpr(Expr *expr);
static void ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
! ExprState *state,
Datum *resv, bool *resnull);
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 122,133 ****
/* Initialize ExprState with empty step list */
state = makeNode(ExprState);
state->expr = node;
/* Insert EEOP_*_FETCHSOME steps as needed */
ExecInitExprSlots(state, (Node *) node);
/* Compile the expression proper */
! ExecInitExprRec(node, parent, state, &state->resvalue, &state->resnull);
/* Finally, append a DONE step */
scratch.opcode = EEOP_DONE;
--- 121,171 ----
/* Initialize ExprState with empty step list */
state = makeNode(ExprState);
state->expr = node;
+ state->parent = parent;
+ state->ext_params = NULL;
/* Insert EEOP_*_FETCHSOME steps as needed */
ExecInitExprSlots(state, (Node *) node);
/* Compile the expression proper */
! ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
!
! /* Finally, append a DONE step */
! scratch.opcode = EEOP_DONE;
! ExprEvalPushStep(state, &scratch);
!
! ExecReadyExpr(state);
!
! return state;
! }
!
! /*
! * ExecInitExprWithParams: prepare a standalone expression tree for execution
! *
! * This is the same as ExecInitExpr, except that there is no parent PlanState,
! * and instead we may have a ParamListInfo describing PARAM_EXTERN Params.
! */
! ExprState *
! ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
! {
! ExprState *state;
! ExprEvalStep scratch;
!
! /* Special case: NULL expression produces a NULL ExprState pointer */
! if (node == NULL)
! return NULL;
!
! /* Initialize ExprState with empty step list */
! state = makeNode(ExprState);
! state->expr = node;
! state->parent = NULL;
! state->ext_params = ext_params;
!
! /* Insert EEOP_*_FETCHSOME steps as needed */
! ExecInitExprSlots(state, (Node *) node);
!
! /* Compile the expression proper */
! ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
/* Finally, append a DONE step */
scratch.opcode = EEOP_DONE;
*************** ExecInitQual(List *qual, PlanState *pare
*** 172,177 ****
--- 210,218 ----
state = makeNode(ExprState);
state->expr = (Expr *) qual;
+ state->parent = parent;
+ state->ext_params = NULL;
+
/* mark expression as to be used with ExecQual() */
state->flags = EEO_FLAG_IS_QUAL;
*************** ExecInitQual(List *qual, PlanState *pare
*** 198,204 ****
Expr *node = (Expr *) lfirst(lc);
/* first evaluate expression */
! ExecInitExprRec(node, parent, state, &state->resvalue, &state->resnull);
/* then emit EEOP_QUAL to detect if it's false (or null) */
scratch.d.qualexpr.jumpdone = -1;
--- 239,245 ----
Expr *node = (Expr *) lfirst(lc);
/* first evaluate expression */
! ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
/* then emit EEOP_QUAL to detect if it's false (or null) */
scratch.d.qualexpr.jumpdone = -1;
*************** ExecBuildProjectionInfo(List *targetList
*** 314,319 ****
--- 355,363 ----
projInfo->pi_state.tag.type = T_ExprState;
state = &projInfo->pi_state;
state->expr = (Expr *) targetList;
+ state->parent = parent;
+ state->ext_params = NULL;
+
state->resultslot = slot;
/* Insert EEOP_*_FETCHSOME steps as needed */
*************** ExecBuildProjectionInfo(List *targetList
*** 398,404 ****
* matter) can change between executions. We instead evaluate
* into the ExprState's resvalue/resnull and then move.
*/
! ExecInitExprRec(tle->expr, parent, state,
&state->resvalue, &state->resnull);
/*
--- 442,448 ----
* matter) can change between executions. We instead evaluate
* into the ExprState's resvalue/resnull and then move.
*/
! ExecInitExprRec(tle->expr, state,
&state->resvalue, &state->resnull);
/*
*************** ExecReadyExpr(ExprState *state)
*** 581,592 ****
* possibly recursing into sub-expressions of node.
*
* node - expression to evaluate
- * parent - parent executor node (or NULL if a standalone expression)
* state - ExprState to whose ->steps to append the necessary operations
* resv / resnull - where to store the result of the node into
*/
static void
! ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
Datum *resv, bool *resnull)
{
ExprEvalStep scratch;
--- 625,635 ----
* possibly recursing into sub-expressions of node.
*
* node - expression to evaluate
* state - ExprState to whose ->steps to append the necessary operations
* resv / resnull - where to store the result of the node into
*/
static void
! ExecInitExprRec(Expr *node, ExprState *state,
Datum *resv, bool *resnull)
{
ExprEvalStep scratch;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 609,615 ****
if (variable->varattno == InvalidAttrNumber)
{
/* whole-row Var */
! ExecInitWholeRowVar(&scratch, variable, parent);
}
else if (variable->varattno <= 0)
{
--- 652,658 ----
if (variable->varattno == InvalidAttrNumber)
{
/* whole-row Var */
! ExecInitWholeRowVar(&scratch, variable, state);
}
else if (variable->varattno <= 0)
{
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 674,679 ****
--- 717,723 ----
case T_Param:
{
Param *param = (Param *) node;
+ ParamListInfo params;
switch (param->paramkind)
{
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 681,699 ****
scratch.opcode = EEOP_PARAM_EXEC;
scratch.d.param.paramid = param->paramid;
scratch.d.param.paramtype = param->paramtype;
break;
case PARAM_EXTERN:
! scratch.opcode = EEOP_PARAM_EXTERN;
! scratch.d.param.paramid = param->paramid;
! scratch.d.param.paramtype = param->paramtype;
break;
default:
elog(ERROR, "unrecognized paramkind: %d",
(int) param->paramkind);
break;
}
-
- ExprEvalPushStep(state, &scratch);
break;
}
--- 725,765 ----
scratch.opcode = EEOP_PARAM_EXEC;
scratch.d.param.paramid = param->paramid;
scratch.d.param.paramtype = param->paramtype;
+ ExprEvalPushStep(state, &scratch);
break;
case PARAM_EXTERN:
!
! /*
! * If we have a relevant ParamCompileHook, use it;
! * otherwise compile a standard EEOP_PARAM_EXTERN
! * step. ext_params, if supplied, takes precedence
! * over info from the parent node's EState (if any).
! */
! if (state->ext_params)
! params = state->ext_params;
! else if (state->parent &&
! state->parent->state)
! params = state->parent->state->es_param_list_info;
! else
! params = NULL;
! if (params && params->paramCompile)
! {
! params->paramCompile(params, param, state,
! resv, resnull);
! }
! else
! {
! scratch.opcode = EEOP_PARAM_EXTERN;
! scratch.d.param.paramid = param->paramid;
! scratch.d.param.paramtype = param->paramtype;
! ExprEvalPushStep(state, &scratch);
! }
break;
default:
elog(ERROR, "unrecognized paramkind: %d",
(int) param->paramkind);
break;
}
break;
}
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 706,714 ****
scratch.d.aggref.astate = astate;
astate->aggref = aggref;
! if (parent && IsA(parent, AggState))
{
! AggState *aggstate = (AggState *) parent;
aggstate->aggs = lcons(astate, aggstate->aggs);
aggstate->numaggs++;
--- 772,780 ----
scratch.d.aggref.astate = astate;
astate->aggref = aggref;
! if (state->parent && IsA(state->parent, AggState))
{
! AggState *aggstate = (AggState *) state->parent;
aggstate->aggs = lcons(astate, aggstate->aggs);
aggstate->numaggs++;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 728,741 ****
GroupingFunc *grp_node = (GroupingFunc *) node;
Agg *agg;
! if (!parent || !IsA(parent, AggState) ||
! !IsA(parent->plan, Agg))
elog(ERROR, "GroupingFunc found in non-Agg plan node");
scratch.opcode = EEOP_GROUPING_FUNC;
! scratch.d.grouping_func.parent = (AggState *) parent;
! agg = (Agg *) (parent->plan);
if (agg->groupingSets)
scratch.d.grouping_func.clauses = grp_node->cols;
--- 794,807 ----
GroupingFunc *grp_node = (GroupingFunc *) node;
Agg *agg;
! if (!state->parent || !IsA(state->parent, AggState) ||
! !IsA(state->parent->plan, Agg))
elog(ERROR, "GroupingFunc found in non-Agg plan node");
scratch.opcode = EEOP_GROUPING_FUNC;
! scratch.d.grouping_func.parent = (AggState *) state->parent;
! agg = (Agg *) (state->parent->plan);
if (agg->groupingSets)
scratch.d.grouping_func.clauses = grp_node->cols;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 753,761 ****
wfstate->wfunc = wfunc;
! if (parent && IsA(parent, WindowAggState))
{
! WindowAggState *winstate = (WindowAggState *) parent;
int nfuncs;
winstate->funcs = lcons(wfstate, winstate->funcs);
--- 819,827 ----
wfstate->wfunc = wfunc;
! if (state->parent && IsA(state->parent, WindowAggState))
{
! WindowAggState *winstate = (WindowAggState *) state->parent;
int nfuncs;
winstate->funcs = lcons(wfstate, winstate->funcs);
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 764,772 ****
winstate->numaggs++;
/* for now initialize agg using old style expressions */
! wfstate->args = ExecInitExprList(wfunc->args, parent);
wfstate->aggfilter = ExecInitExpr(wfunc->aggfilter,
! parent);
/*
* Complain if the windowfunc's arguments contain any
--- 830,839 ----
winstate->numaggs++;
/* for now initialize agg using old style expressions */
! wfstate->args = ExecInitExprList(wfunc->args,
! state->parent);
wfstate->aggfilter = ExecInitExpr(wfunc->aggfilter,
! state->parent);
/*
* Complain if the windowfunc's arguments contain any
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 795,801 ****
{
ArrayRef *aref = (ArrayRef *) node;
! ExecInitArrayRef(&scratch, aref, parent, state, resv, resnull);
break;
}
--- 862,868 ----
{
ArrayRef *aref = (ArrayRef *) node;
! ExecInitArrayRef(&scratch, aref, state, resv, resnull);
break;
}
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 805,811 ****
ExecInitFunc(&scratch, node,
func->args, func->funcid, func->inputcollid,
! parent, state);
ExprEvalPushStep(state, &scratch);
break;
}
--- 872,878 ----
ExecInitFunc(&scratch, node,
func->args, func->funcid, func->inputcollid,
! state);
ExprEvalPushStep(state, &scratch);
break;
}
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 816,822 ****
ExecInitFunc(&scratch, node,
op->args, op->opfuncid, op->inputcollid,
! parent, state);
ExprEvalPushStep(state, &scratch);
break;
}
--- 883,889 ----
ExecInitFunc(&scratch, node,
op->args, op->opfuncid, op->inputcollid,
! state);
ExprEvalPushStep(state, &scratch);
break;
}
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 827,833 ****
ExecInitFunc(&scratch, node,
op->args, op->opfuncid, op->inputcollid,
! parent, state);
/*
* Change opcode of call instruction to EEOP_DISTINCT.
--- 894,900 ----
ExecInitFunc(&scratch, node,
op->args, op->opfuncid, op->inputcollid,
! state);
/*
* Change opcode of call instruction to EEOP_DISTINCT.
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 849,855 ****
ExecInitFunc(&scratch, node,
op->args, op->opfuncid, op->inputcollid,
! parent, state);
/*
* Change opcode of call instruction to EEOP_NULLIF.
--- 916,922 ----
ExecInitFunc(&scratch, node,
op->args, op->opfuncid, op->inputcollid,
! state);
/*
* Change opcode of call instruction to EEOP_NULLIF.
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 896,902 ****
opexpr->inputcollid, NULL, NULL);
/* Evaluate scalar directly into left function argument */
! ExecInitExprRec(scalararg, parent, state,
&fcinfo->arg[0], &fcinfo->argnull[0]);
/*
--- 963,969 ----
opexpr->inputcollid, NULL, NULL);
/* Evaluate scalar directly into left function argument */
! ExecInitExprRec(scalararg, state,
&fcinfo->arg[0], &fcinfo->argnull[0]);
/*
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 905,911 ****
* be overwritten by EEOP_SCALARARRAYOP, and will not be
* passed to any other expression.
*/
! ExecInitExprRec(arrayarg, parent, state, resv, resnull);
/* And perform the operation */
scratch.opcode = EEOP_SCALARARRAYOP;
--- 972,978 ----
* be overwritten by EEOP_SCALARARRAYOP, and will not be
* passed to any other expression.
*/
! ExecInitExprRec(arrayarg, state, resv, resnull);
/* And perform the operation */
scratch.opcode = EEOP_SCALARARRAYOP;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 949,955 ****
Expr *arg = (Expr *) lfirst(lc);
/* Evaluate argument into our output variable */
! ExecInitExprRec(arg, parent, state, resv, resnull);
/* Perform the appropriate step type */
switch (boolexpr->boolop)
--- 1016,1022 ----
Expr *arg = (Expr *) lfirst(lc);
/* Evaluate argument into our output variable */
! ExecInitExprRec(arg, state, resv, resnull);
/* Perform the appropriate step type */
switch (boolexpr->boolop)
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1009,1021 ****
SubPlan *subplan = (SubPlan *) node;
SubPlanState *sstate;
! if (!parent)
elog(ERROR, "SubPlan found with no parent plan");
! sstate = ExecInitSubPlan(subplan, parent);
! /* add SubPlanState nodes to parent->subPlan */
! parent->subPlan = lappend(parent->subPlan, sstate);
scratch.opcode = EEOP_SUBPLAN;
scratch.d.subplan.sstate = sstate;
--- 1076,1089 ----
SubPlan *subplan = (SubPlan *) node;
SubPlanState *sstate;
! if (!state->parent)
elog(ERROR, "SubPlan found with no parent plan");
! sstate = ExecInitSubPlan(subplan, state->parent);
! /* add SubPlanState nodes to state->parent->subPlan */
! state->parent->subPlan = lappend(state->parent->subPlan,
! sstate);
scratch.opcode = EEOP_SUBPLAN;
scratch.d.subplan.sstate = sstate;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1029,1038 ****
AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
AlternativeSubPlanState *asstate;
! if (!parent)
elog(ERROR, "AlternativeSubPlan found with no parent plan");
! asstate = ExecInitAlternativeSubPlan(asplan, parent);
scratch.opcode = EEOP_ALTERNATIVE_SUBPLAN;
scratch.d.alternative_subplan.asstate = asstate;
--- 1097,1106 ----
AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
AlternativeSubPlanState *asstate;
! if (!state->parent)
elog(ERROR, "AlternativeSubPlan found with no parent plan");
! asstate = ExecInitAlternativeSubPlan(asplan, state->parent);
scratch.opcode = EEOP_ALTERNATIVE_SUBPLAN;
scratch.d.alternative_subplan.asstate = asstate;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1046,1052 ****
FieldSelect *fselect = (FieldSelect *) node;
/* evaluate row/record argument into result area */
! ExecInitExprRec(fselect->arg, parent, state, resv, resnull);
/* and extract field */
scratch.opcode = EEOP_FIELDSELECT;
--- 1114,1120 ----
FieldSelect *fselect = (FieldSelect *) node;
/* evaluate row/record argument into result area */
! ExecInitExprRec(fselect->arg, state, resv, resnull);
/* and extract field */
scratch.opcode = EEOP_FIELDSELECT;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1083,1089 ****
*descp = NULL;
/* emit code to evaluate the composite input value */
! ExecInitExprRec(fstore->arg, parent, state, resv, resnull);
/* next, deform the input tuple into our workspace */
scratch.opcode = EEOP_FIELDSTORE_DEFORM;
--- 1151,1157 ----
*descp = NULL;
/* emit code to evaluate the composite input value */
! ExecInitExprRec(fstore->arg, state, resv, resnull);
/* next, deform the input tuple into our workspace */
scratch.opcode = EEOP_FIELDSTORE_DEFORM;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1134,1140 ****
state->innermost_caseval = &values[fieldnum - 1];
state->innermost_casenull = &nulls[fieldnum - 1];
! ExecInitExprRec(e, parent, state,
&values[fieldnum - 1],
&nulls[fieldnum - 1]);
--- 1202,1208 ----
state->innermost_caseval = &values[fieldnum - 1];
state->innermost_casenull = &nulls[fieldnum - 1];
! ExecInitExprRec(e, state,
&values[fieldnum - 1],
&nulls[fieldnum - 1]);
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1158,1164 ****
/* relabel doesn't need to do anything at runtime */
RelabelType *relabel = (RelabelType *) node;
! ExecInitExprRec(relabel->arg, parent, state, resv, resnull);
break;
}
--- 1226,1232 ----
/* relabel doesn't need to do anything at runtime */
RelabelType *relabel = (RelabelType *) node;
! ExecInitExprRec(relabel->arg, state, resv, resnull);
break;
}
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1171,1177 ****
FunctionCallInfo fcinfo_in;
/* evaluate argument into step's result area */
! ExecInitExprRec(iocoerce->arg, parent, state, resv, resnull);
/*
* Prepare both output and input function calls, to be
--- 1239,1245 ----
FunctionCallInfo fcinfo_in;
/* evaluate argument into step's result area */
! ExecInitExprRec(iocoerce->arg, state, resv, resnull);
/*
* Prepare both output and input function calls, to be
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1228,1234 ****
ExprState *elemstate;
/* evaluate argument into step's result area */
! ExecInitExprRec(acoerce->arg, parent, state, resv, resnull);
resultelemtype = get_element_type(acoerce->resulttype);
if (!OidIsValid(resultelemtype))
--- 1296,1302 ----
ExprState *elemstate;
/* evaluate argument into step's result area */
! ExecInitExprRec(acoerce->arg, state, resv, resnull);
resultelemtype = get_element_type(acoerce->resulttype);
if (!OidIsValid(resultelemtype))
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1244,1253 ****
*/
elemstate = makeNode(ExprState);
elemstate->expr = acoerce->elemexpr;
elemstate->innermost_caseval = (Datum *) palloc(sizeof(Datum));
elemstate->innermost_casenull = (bool *) palloc(sizeof(bool));
! ExecInitExprRec(acoerce->elemexpr, parent, elemstate,
&elemstate->resvalue, &elemstate->resnull);
if (elemstate->steps_len == 1 &&
--- 1312,1324 ----
*/
elemstate = makeNode(ExprState);
elemstate->expr = acoerce->elemexpr;
+ elemstate->parent = state->parent;
+ elemstate->ext_params = state->ext_params;
+
elemstate->innermost_caseval = (Datum *) palloc(sizeof(Datum));
elemstate->innermost_casenull = (bool *) palloc(sizeof(bool));
! ExecInitExprRec(acoerce->elemexpr, elemstate,
&elemstate->resvalue, &elemstate->resnull);
if (elemstate->steps_len == 1 &&
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1290,1296 ****
ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
/* evaluate argument into step's result area */
! ExecInitExprRec(convert->arg, parent, state, resv, resnull);
/* and push conversion step */
scratch.opcode = EEOP_CONVERT_ROWTYPE;
--- 1361,1367 ----
ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
/* evaluate argument into step's result area */
! ExecInitExprRec(convert->arg, state, resv, resnull);
/* and push conversion step */
scratch.opcode = EEOP_CONVERT_ROWTYPE;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1324,1330 ****
caseval = palloc(sizeof(Datum));
casenull = palloc(sizeof(bool));
! ExecInitExprRec(caseExpr->arg, parent, state,
caseval, casenull);
/*
--- 1395,1401 ----
caseval = palloc(sizeof(Datum));
casenull = palloc(sizeof(bool));
! ExecInitExprRec(caseExpr->arg, state,
caseval, casenull);
/*
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1375,1381 ****
state->innermost_casenull = casenull;
/* evaluate condition into CASE's result variables */
! ExecInitExprRec(when->expr, parent, state, resv, resnull);
state->innermost_caseval = save_innermost_caseval;
state->innermost_casenull = save_innermost_casenull;
--- 1446,1452 ----
state->innermost_casenull = casenull;
/* evaluate condition into CASE's result variables */
! ExecInitExprRec(when->expr, state, resv, resnull);
state->innermost_caseval = save_innermost_caseval;
state->innermost_casenull = save_innermost_casenull;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1390,1396 ****
* If WHEN result is true, evaluate THEN result, storing
* it into the CASE's result variables.
*/
! ExecInitExprRec(when->result, parent, state, resv, resnull);
/* Emit JUMP step to jump to end of CASE's code */
scratch.opcode = EEOP_JUMP;
--- 1461,1467 ----
* If WHEN result is true, evaluate THEN result, storing
* it into the CASE's result variables.
*/
! ExecInitExprRec(when->result, state, resv, resnull);
/* Emit JUMP step to jump to end of CASE's code */
scratch.opcode = EEOP_JUMP;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1415,1421 ****
Assert(caseExpr->defresult);
/* evaluate ELSE expr into CASE's result variables */
! ExecInitExprRec(caseExpr->defresult, parent, state,
resv, resnull);
/* adjust jump targets */
--- 1486,1492 ----
Assert(caseExpr->defresult);
/* evaluate ELSE expr into CASE's result variables */
! ExecInitExprRec(caseExpr->defresult, state,
resv, resnull);
/* adjust jump targets */
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1484,1490 ****
{
Expr *e = (Expr *) lfirst(lc);
! ExecInitExprRec(e, parent, state,
&scratch.d.arrayexpr.elemvalues[elemoff],
&scratch.d.arrayexpr.elemnulls[elemoff]);
elemoff++;
--- 1555,1561 ----
{
Expr *e = (Expr *) lfirst(lc);
! ExecInitExprRec(e, state,
&scratch.d.arrayexpr.elemvalues[elemoff],
&scratch.d.arrayexpr.elemnulls[elemoff]);
elemoff++;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1578,1584 ****
}
/* Evaluate column expr into appropriate workspace slot */
! ExecInitExprRec(e, parent, state,
&scratch.d.row.elemvalues[i],
&scratch.d.row.elemnulls[i]);
i++;
--- 1649,1655 ----
}
/* Evaluate column expr into appropriate workspace slot */
! ExecInitExprRec(e, state,
&scratch.d.row.elemvalues[i],
&scratch.d.row.elemnulls[i]);
i++;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1667,1675 ****
*/
/* evaluate left and right args directly into fcinfo */
! ExecInitExprRec(left_expr, parent, state,
&fcinfo->arg[0], &fcinfo->argnull[0]);
! ExecInitExprRec(right_expr, parent, state,
&fcinfo->arg[1], &fcinfo->argnull[1]);
scratch.opcode = EEOP_ROWCOMPARE_STEP;
--- 1738,1746 ----
*/
/* evaluate left and right args directly into fcinfo */
! ExecInitExprRec(left_expr, state,
&fcinfo->arg[0], &fcinfo->argnull[0]);
! ExecInitExprRec(right_expr, state,
&fcinfo->arg[1], &fcinfo->argnull[1]);
scratch.opcode = EEOP_ROWCOMPARE_STEP;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1738,1744 ****
Expr *e = (Expr *) lfirst(lc);
/* evaluate argument, directly into result datum */
! ExecInitExprRec(e, parent, state, resv, resnull);
/* if it's not null, skip to end of COALESCE expr */
scratch.opcode = EEOP_JUMP_IF_NOT_NULL;
--- 1809,1815 ----
Expr *e = (Expr *) lfirst(lc);
/* evaluate argument, directly into result datum */
! ExecInitExprRec(e, state, resv, resnull);
/* if it's not null, skip to end of COALESCE expr */
scratch.opcode = EEOP_JUMP_IF_NOT_NULL;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1820,1826 ****
{
Expr *e = (Expr *) lfirst(lc);
! ExecInitExprRec(e, parent, state,
&scratch.d.minmax.values[off],
&scratch.d.minmax.nulls[off]);
off++;
--- 1891,1897 ----
{
Expr *e = (Expr *) lfirst(lc);
! ExecInitExprRec(e, state,
&scratch.d.minmax.values[off],
&scratch.d.minmax.nulls[off]);
off++;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1886,1892 ****
{
Expr *e = (Expr *) lfirst(arg);
! ExecInitExprRec(e, parent, state,
&scratch.d.xmlexpr.named_argvalue[off],
&scratch.d.xmlexpr.named_argnull[off]);
off++;
--- 1957,1963 ----
{
Expr *e = (Expr *) lfirst(arg);
! ExecInitExprRec(e, state,
&scratch.d.xmlexpr.named_argvalue[off],
&scratch.d.xmlexpr.named_argnull[off]);
off++;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1897,1903 ****
{
Expr *e = (Expr *) lfirst(arg);
! ExecInitExprRec(e, parent, state,
&scratch.d.xmlexpr.argvalue[off],
&scratch.d.xmlexpr.argnull[off]);
off++;
--- 1968,1974 ----
{
Expr *e = (Expr *) lfirst(arg);
! ExecInitExprRec(e, state,
&scratch.d.xmlexpr.argvalue[off],
&scratch.d.xmlexpr.argnull[off]);
off++;
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1935,1941 ****
scratch.d.nulltest_row.argdesc = NULL;
/* first evaluate argument into result variable */
! ExecInitExprRec(ntest->arg, parent, state,
resv, resnull);
/* then push the test of that argument */
--- 2006,2012 ----
scratch.d.nulltest_row.argdesc = NULL;
/* first evaluate argument into result variable */
! ExecInitExprRec(ntest->arg, state,
resv, resnull);
/* then push the test of that argument */
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1953,1959 ****
* and will get overwritten by the below EEOP_BOOLTEST_IS_*
* step.
*/
! ExecInitExprRec(btest->arg, parent, state, resv, resnull);
switch (btest->booltesttype)
{
--- 2024,2030 ----
* and will get overwritten by the below EEOP_BOOLTEST_IS_*
* step.
*/
! ExecInitExprRec(btest->arg, state, resv, resnull);
switch (btest->booltesttype)
{
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 1990,1996 ****
{
CoerceToDomain *ctest = (CoerceToDomain *) node;
! ExecInitCoerceToDomain(&scratch, ctest, parent, state,
resv, resnull);
break;
}
--- 2061,2067 ----
{
CoerceToDomain *ctest = (CoerceToDomain *) node;
! ExecInitCoerceToDomain(&scratch, ctest, state,
resv, resnull);
break;
}
*************** ExecInitExprRec(Expr *node, PlanState *p
*** 2046,2052 ****
* Note that this potentially re-allocates es->steps, therefore no pointer
* into that array may be used while the expression is still being built.
*/
! static void
ExprEvalPushStep(ExprState *es, const ExprEvalStep *s)
{
if (es->steps_alloc == 0)
--- 2117,2123 ----
* Note that this potentially re-allocates es->steps, therefore no pointer
* into that array may be used while the expression is still being built.
*/
! void
ExprEvalPushStep(ExprState *es, const ExprEvalStep *s)
{
if (es->steps_alloc == 0)
*************** ExprEvalPushStep(ExprState *es, const Ex
*** 2074,2080 ****
*/
static void
ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
! Oid inputcollid, PlanState *parent, ExprState *state)
{
int nargs = list_length(args);
AclResult aclresult;
--- 2145,2151 ----
*/
static void
ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
! Oid inputcollid, ExprState *state)
{
int nargs = list_length(args);
AclResult aclresult;
*************** ExecInitFunc(ExprEvalStep *scratch, Expr
*** 2126,2133 ****
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("set-valued function called in context that cannot accept a set"),
! parent ? executor_errposition(parent->state,
! exprLocation((Node *) node)) : 0));
/* Build code to evaluate arguments directly into the fcinfo struct */
argno = 0;
--- 2197,2205 ----
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("set-valued function called in context that cannot accept a set"),
! state->parent ?
! executor_errposition(state->parent->state,
! exprLocation((Node *) node)) : 0));
/* Build code to evaluate arguments directly into the fcinfo struct */
argno = 0;
*************** ExecInitFunc(ExprEvalStep *scratch, Expr
*** 2148,2154 ****
}
else
{
! ExecInitExprRec(arg, parent, state,
&fcinfo->arg[argno], &fcinfo->argnull[argno]);
}
argno++;
--- 2220,2226 ----
}
else
{
! ExecInitExprRec(arg, state,
&fcinfo->arg[argno], &fcinfo->argnull[argno]);
}
argno++;
*************** get_last_attnums_walker(Node *node, Last
*** 2260,2266 ****
* The caller still has to push the step.
*/
static void
! ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, PlanState *parent)
{
/* fill in all but the target */
scratch->opcode = EEOP_WHOLEROW;
--- 2332,2338 ----
* The caller still has to push the step.
*/
static void
! ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, ExprState *state)
{
/* fill in all but the target */
scratch->opcode = EEOP_WHOLEROW;
*************** ExecInitWholeRowVar(ExprEvalStep *scratc
*** 2282,2298 ****
* can occur in such expressions, but they will always be referencing
* table rows.)
*/
! if (parent)
{
PlanState *subplan = NULL;
! switch (nodeTag(parent))
{
case T_SubqueryScanState:
! subplan = ((SubqueryScanState *) parent)->subplan;
break;
case T_CteScanState:
! subplan = ((CteScanState *) parent)->cteplanstate;
break;
default:
break;
--- 2354,2370 ----
* can occur in such expressions, but they will always be referencing
* table rows.)
*/
! if (state->parent)
{
PlanState *subplan = NULL;
! switch (nodeTag(state->parent))
{
case T_SubqueryScanState:
! subplan = ((SubqueryScanState *) state->parent)->subplan;
break;
case T_CteScanState:
! subplan = ((CteScanState *) state->parent)->cteplanstate;
break;
default:
break;
*************** ExecInitWholeRowVar(ExprEvalStep *scratc
*** 2321,2327 ****
scratch->d.wholerow.junkFilter =
ExecInitJunkFilter(subplan->plan->targetlist,
ExecGetResultType(subplan)->tdhasoid,
! ExecInitExtraTupleSlot(parent->state));
}
}
}
--- 2393,2399 ----
scratch->d.wholerow.junkFilter =
ExecInitJunkFilter(subplan->plan->targetlist,
ExecGetResultType(subplan)->tdhasoid,
! ExecInitExtraTupleSlot(state->parent->state));
}
}
}
*************** ExecInitWholeRowVar(ExprEvalStep *scratc
*** 2331,2337 ****
* Prepare evaluation of an ArrayRef expression.
*/
static void
! ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent,
ExprState *state, Datum *resv, bool *resnull)
{
bool isAssignment = (aref->refassgnexpr != NULL);
--- 2403,2409 ----
* Prepare evaluation of an ArrayRef expression.
*/
static void
! ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref,
ExprState *state, Datum *resv, bool *resnull)
{
bool isAssignment = (aref->refassgnexpr != NULL);
*************** ExecInitArrayRef(ExprEvalStep *scratch,
*** 2355,2361 ****
* be overwritten by the final EEOP_ARRAYREF_FETCH/ASSIGN step, which is
* pushed last.
*/
! ExecInitExprRec(aref->refexpr, parent, state, resv, resnull);
/*
* If refexpr yields NULL, and it's a fetch, then result is NULL. We can
--- 2427,2433 ----
* be overwritten by the final EEOP_ARRAYREF_FETCH/ASSIGN step, which is
* pushed last.
*/
! ExecInitExprRec(aref->refexpr, state, resv, resnull);
/*
* If refexpr yields NULL, and it's a fetch, then result is NULL. We can
*************** ExecInitArrayRef(ExprEvalStep *scratch,
*** 2401,2407 ****
arefstate->upperprovided[i] = true;
/* Each subscript is evaluated into subscriptvalue/subscriptnull */
! ExecInitExprRec(e, parent, state,
&arefstate->subscriptvalue, &arefstate->subscriptnull);
/* ... and then ARRAYREF_SUBSCRIPT saves it into step's workspace */
--- 2473,2479 ----
arefstate->upperprovided[i] = true;
/* Each subscript is evaluated into subscriptvalue/subscriptnull */
! ExecInitExprRec(e, state,
&arefstate->subscriptvalue, &arefstate->subscriptnull);
/* ... and then ARRAYREF_SUBSCRIPT saves it into step's workspace */
*************** ExecInitArrayRef(ExprEvalStep *scratch,
*** 2434,2440 ****
arefstate->lowerprovided[i] = true;
/* Each subscript is evaluated into subscriptvalue/subscriptnull */
! ExecInitExprRec(e, parent, state,
&arefstate->subscriptvalue, &arefstate->subscriptnull);
/* ... and then ARRAYREF_SUBSCRIPT saves it into step's workspace */
--- 2506,2512 ----
arefstate->lowerprovided[i] = true;
/* Each subscript is evaluated into subscriptvalue/subscriptnull */
! ExecInitExprRec(e, state,
&arefstate->subscriptvalue, &arefstate->subscriptnull);
/* ... and then ARRAYREF_SUBSCRIPT saves it into step's workspace */
*************** ExecInitArrayRef(ExprEvalStep *scratch,
*** 2488,2494 ****
state->innermost_casenull = &arefstate->prevnull;
/* evaluate replacement value into replacevalue/replacenull */
! ExecInitExprRec(aref->refassgnexpr, parent, state,
&arefstate->replacevalue, &arefstate->replacenull);
state->innermost_caseval = save_innermost_caseval;
--- 2560,2566 ----
state->innermost_casenull = &arefstate->prevnull;
/* evaluate replacement value into replacevalue/replacenull */
! ExecInitExprRec(aref->refassgnexpr, state,
&arefstate->replacevalue, &arefstate->replacenull);
state->innermost_caseval = save_innermost_caseval;
*************** isAssignmentIndirectionExpr(Expr *expr)
*** 2566,2573 ****
*/
static void
ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
! PlanState *parent, ExprState *state,
! Datum *resv, bool *resnull)
{
ExprEvalStep scratch2;
DomainConstraintRef *constraint_ref;
--- 2638,2644 ----
*/
static void
ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
! ExprState *state, Datum *resv, bool *resnull)
{
ExprEvalStep scratch2;
DomainConstraintRef *constraint_ref;
*************** ExecInitCoerceToDomain(ExprEvalStep *scr
*** 2587,2593 ****
* if there's constraint failures there'll be errors, otherwise it's what
* needs to be returned.
*/
! ExecInitExprRec(ctest->arg, parent, state, resv, resnull);
/*
* Note: if the argument is of varlena type, it could be a R/W expanded
--- 2658,2664 ----
* if there's constraint failures there'll be errors, otherwise it's what
* needs to be returned.
*/
! ExecInitExprRec(ctest->arg, state, resv, resnull);
/*
* Note: if the argument is of varlena type, it could be a R/W expanded
*************** ExecInitCoerceToDomain(ExprEvalStep *scr
*** 2684,2690 ****
state->innermost_domainnull = domainnull;
/* evaluate check expression value */
! ExecInitExprRec(con->check_expr, parent, state,
scratch->d.domaincheck.checkvalue,
scratch->d.domaincheck.checknull);
--- 2755,2761 ----
state->innermost_domainnull = domainnull;
/* evaluate check expression value */
! ExecInitExprRec(con->check_expr, state,
scratch->d.domaincheck.checkvalue,
scratch->d.domaincheck.checknull);
diff --git a/src/backend/executor/execExprInterp.c b/src/backend/executor/execExprInterp.c
index 6c4612d..0c3f668 100644
*** a/src/backend/executor/execExprInterp.c
--- b/src/backend/executor/execExprInterp.c
*************** ExecInterpExpr(ExprState *state, ExprCon
*** 335,340 ****
--- 335,341 ----
&&CASE_EEOP_BOOLTEST_IS_NOT_FALSE,
&&CASE_EEOP_PARAM_EXEC,
&&CASE_EEOP_PARAM_EXTERN,
+ &&CASE_EEOP_PARAM_CALLBACK,
&&CASE_EEOP_CASE_TESTVAL,
&&CASE_EEOP_MAKE_READONLY,
&&CASE_EEOP_IOCOERCE,
*************** ExecInterpExpr(ExprState *state, ExprCon
*** 1047,1052 ****
--- 1048,1060 ----
EEO_NEXT();
}
+ EEO_CASE(EEOP_PARAM_CALLBACK)
+ {
+ /* allow an extension module to supply a PARAM_EXTERN value */
+ op->d.cparam.paramfunc(state, op, econtext);
+ EEO_NEXT();
+ }
+
EEO_CASE(EEOP_CASE_TESTVAL)
{
/*
*************** ExecEvalParamExtern(ExprState *state, Ex
*** 1967,1977 ****
if (likely(paramInfo &&
paramId > 0 && paramId <= paramInfo->numParams))
{
! ParamExternData *prm = ¶mInfo->params[paramId - 1];
/* give hook a chance in case parameter is dynamic */
! if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
! paramInfo->paramFetch(paramInfo, paramId);
if (likely(OidIsValid(prm->ptype)))
{
--- 1975,1988 ----
if (likely(paramInfo &&
paramId > 0 && paramId <= paramInfo->numParams))
{
! ParamExternData *prm;
! ParamExternData prmdata;
/* give hook a chance in case parameter is dynamic */
! if (paramInfo->paramFetch != NULL)
! prm = paramInfo->paramFetch(paramInfo, paramId, false, &prmdata);
! else
! prm = ¶mInfo->params[paramId - 1];
if (likely(OidIsValid(prm->ptype)))
{
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index 3caa343..527f7d8 100644
*** a/src/backend/executor/functions.c
--- b/src/backend/executor/functions.c
*************** postquel_sub_params(SQLFunctionCachePtr
*** 914,923 ****
/* we have static list of params, so no hooks needed */
paramLI->paramFetch = NULL;
paramLI->paramFetchArg = NULL;
paramLI->parserSetup = NULL;
paramLI->parserSetupArg = NULL;
paramLI->numParams = nargs;
- paramLI->paramMask = NULL;
fcache->paramLI = paramLI;
}
else
--- 914,924 ----
/* we have static list of params, so no hooks needed */
paramLI->paramFetch = NULL;
paramLI->paramFetchArg = NULL;
+ paramLI->paramCompile = NULL;
+ paramLI->paramCompileArg = NULL;
paramLI->parserSetup = NULL;
paramLI->parserSetupArg = NULL;
paramLI->numParams = nargs;
fcache->paramLI = paramLI;
}
else
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index f3da2dd..977f317 100644
*** a/src/backend/executor/spi.c
--- b/src/backend/executor/spi.c
*************** _SPI_convert_params(int nargs, Oid *argt
*** 2259,2268 ****
/* we have static list of params, so no hooks needed */
paramLI->paramFetch = NULL;
paramLI->paramFetchArg = NULL;
paramLI->parserSetup = NULL;
paramLI->parserSetupArg = NULL;
paramLI->numParams = nargs;
- paramLI->paramMask = NULL;
for (i = 0; i < nargs; i++)
{
--- 2259,2269 ----
/* we have static list of params, so no hooks needed */
paramLI->paramFetch = NULL;
paramLI->paramFetchArg = NULL;
+ paramLI->paramCompile = NULL;
+ paramLI->paramCompileArg = NULL;
paramLI->parserSetup = NULL;
paramLI->parserSetupArg = NULL;
paramLI->numParams = nargs;
for (i = 0; i < nargs; i++)
{
diff --git a/src/backend/nodes/params.c b/src/backend/nodes/params.c
index 51429af..94acdf4 100644
*** a/src/backend/nodes/params.c
--- b/src/backend/nodes/params.c
*************** copyParamList(ParamListInfo from)
*** 48,79 ****
retval = (ParamListInfo) palloc(size);
retval->paramFetch = NULL;
retval->paramFetchArg = NULL;
retval->parserSetup = NULL;
retval->parserSetupArg = NULL;
retval->numParams = from->numParams;
- retval->paramMask = NULL;
for (i = 0; i < from->numParams; i++)
{
! ParamExternData *oprm = &from->params[i];
ParamExternData *nprm = &retval->params[i];
int16 typLen;
bool typByVal;
- /* Ignore parameters we don't need, to save cycles and space. */
- if (from->paramMask != NULL &&
- !bms_is_member(i, from->paramMask))
- {
- nprm->value = (Datum) 0;
- nprm->isnull = true;
- nprm->pflags = 0;
- nprm->ptype = InvalidOid;
- continue;
- }
-
/* give hook a chance in case parameter is dynamic */
! if (!OidIsValid(oprm->ptype) && from->paramFetch != NULL)
! from->paramFetch(from, i + 1);
/* flat-copy the parameter info */
*nprm = *oprm;
--- 48,72 ----
retval = (ParamListInfo) palloc(size);
retval->paramFetch = NULL;
retval->paramFetchArg = NULL;
+ retval->paramCompile = NULL;
+ retval->paramCompileArg = NULL;
retval->parserSetup = NULL;
retval->parserSetupArg = NULL;
retval->numParams = from->numParams;
for (i = 0; i < from->numParams; i++)
{
! ParamExternData *oprm;
ParamExternData *nprm = &retval->params[i];
+ ParamExternData prmdata;
int16 typLen;
bool typByVal;
/* give hook a chance in case parameter is dynamic */
! if (from->paramFetch != NULL)
! oprm = from->paramFetch(from, i + 1, false, &prmdata);
! else
! oprm = &from->params[i];
/* flat-copy the parameter info */
*nprm = *oprm;
*************** EstimateParamListSpace(ParamListInfo par
*** 102,123 ****
for (i = 0; i < paramLI->numParams; i++)
{
! ParamExternData *prm = ¶mLI->params[i];
Oid typeOid;
int16 typLen;
bool typByVal;
! /* Ignore parameters we don't need, to save cycles and space. */
! if (paramLI->paramMask != NULL &&
! !bms_is_member(i, paramLI->paramMask))
! typeOid = InvalidOid;
else
! {
! /* give hook a chance in case parameter is dynamic */
! if (!OidIsValid(prm->ptype) && paramLI->paramFetch != NULL)
! paramLI->paramFetch(paramLI, i + 1);
! typeOid = prm->ptype;
! }
sz = add_size(sz, sizeof(Oid)); /* space for type OID */
sz = add_size(sz, sizeof(uint16)); /* space for pflags */
--- 95,113 ----
for (i = 0; i < paramLI->numParams; i++)
{
! ParamExternData *prm;
! ParamExternData prmdata;
Oid typeOid;
int16 typLen;
bool typByVal;
! /* give hook a chance in case parameter is dynamic */
! if (paramLI->paramFetch != NULL)
! prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata);
else
! prm = ¶mLI->params[i];
!
! typeOid = prm->ptype;
sz = add_size(sz, sizeof(Oid)); /* space for type OID */
sz = add_size(sz, sizeof(uint16)); /* space for pflags */
*************** SerializeParamList(ParamListInfo paramLI
*** 171,192 ****
/* Write each parameter in turn. */
for (i = 0; i < nparams; i++)
{
! ParamExternData *prm = ¶mLI->params[i];
Oid typeOid;
int16 typLen;
bool typByVal;
! /* Ignore parameters we don't need, to save cycles and space. */
! if (paramLI->paramMask != NULL &&
! !bms_is_member(i, paramLI->paramMask))
! typeOid = InvalidOid;
else
! {
! /* give hook a chance in case parameter is dynamic */
! if (!OidIsValid(prm->ptype) && paramLI->paramFetch != NULL)
! paramLI->paramFetch(paramLI, i + 1);
! typeOid = prm->ptype;
! }
/* Write type OID. */
memcpy(*start_address, &typeOid, sizeof(Oid));
--- 161,179 ----
/* Write each parameter in turn. */
for (i = 0; i < nparams; i++)
{
! ParamExternData *prm;
! ParamExternData prmdata;
Oid typeOid;
int16 typLen;
bool typByVal;
! /* give hook a chance in case parameter is dynamic */
! if (paramLI->paramFetch != NULL)
! prm = paramLI->paramFetch(paramLI, i + 1, false, &prmdata);
else
! prm = ¶mLI->params[i];
!
! typeOid = prm->ptype;
/* Write type OID. */
memcpy(*start_address, &typeOid, sizeof(Oid));
*************** RestoreParamList(char **start_address)
*** 237,246 ****
paramLI = (ParamListInfo) palloc(size);
paramLI->paramFetch = NULL;
paramLI->paramFetchArg = NULL;
paramLI->parserSetup = NULL;
paramLI->parserSetupArg = NULL;
paramLI->numParams = nparams;
- paramLI->paramMask = NULL;
for (i = 0; i < nparams; i++)
{
--- 224,234 ----
paramLI = (ParamListInfo) palloc(size);
paramLI->paramFetch = NULL;
paramLI->paramFetchArg = NULL;
+ paramLI->paramCompile = NULL;
+ paramLI->paramCompileArg = NULL;
paramLI->parserSetup = NULL;
paramLI->parserSetupArg = NULL;
paramLI->numParams = nparams;
for (i = 0; i < nparams; i++)
{
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 6a2d5ad..9ca384d 100644
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
*************** eval_const_expressions_mutator(Node *nod
*** 2513,2526 ****
case T_Param:
{
Param *param = (Param *) node;
/* Look to see if we've been given a value for this Param */
if (param->paramkind == PARAM_EXTERN &&
! context->boundParams != NULL &&
param->paramid > 0 &&
! param->paramid <= context->boundParams->numParams)
{
! ParamExternData *prm = &context->boundParams->params[param->paramid - 1];
if (OidIsValid(prm->ptype))
{
--- 2513,2539 ----
case T_Param:
{
Param *param = (Param *) node;
+ ParamListInfo paramLI = context->boundParams;
/* Look to see if we've been given a value for this Param */
if (param->paramkind == PARAM_EXTERN &&
! paramLI != NULL &&
param->paramid > 0 &&
! param->paramid <= paramLI->numParams)
{
! ParamExternData *prm;
! ParamExternData prmdata;
!
! /*
! * Give hook a chance in case parameter is dynamic. Tell
! * it that this fetch is speculative, so it should avoid
! * erroring out if parameter is unavailable.
! */
! if (paramLI->paramFetch != NULL)
! prm = paramLI->paramFetch(paramLI, param->paramid,
! true, &prmdata);
! else
! prm = ¶mLI->params[param->paramid - 1];
if (OidIsValid(prm->ptype))
{
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 1ae9ac2..1b24ddd 100644
*** a/src/backend/tcop/postgres.c
--- b/src/backend/tcop/postgres.c
*************** exec_bind_message(StringInfo input_messa
*** 1646,1655 ****
/* we have static list of params, so no hooks needed */
params->paramFetch = NULL;
params->paramFetchArg = NULL;
params->parserSetup = NULL;
params->parserSetupArg = NULL;
params->numParams = numParams;
- params->paramMask = NULL;
for (paramno = 0; paramno < numParams; paramno++)
{
--- 1646,1656 ----
/* we have static list of params, so no hooks needed */
params->paramFetch = NULL;
params->paramFetchArg = NULL;
+ params->paramCompile = NULL;
+ params->paramCompileArg = NULL;
params->parserSetup = NULL;
params->parserSetupArg = NULL;
params->numParams = numParams;
for (paramno = 0; paramno < numParams; paramno++)
{
*************** errdetail_params(ParamListInfo params)
*** 2211,2216 ****
--- 2212,2220 ----
MemoryContext oldcontext;
int paramno;
+ /* This code doesn't support dynamic param lists */
+ Assert(params->paramFetch == NULL);
+
/* Make sure any trash is generated in MessageContext */
oldcontext = MemoryContextSwitchTo(MessageContext);
diff --git a/src/include/executor/execExpr.h b/src/include/executor/execExpr.h
index 5bbb63a..080252f 100644
*** a/src/include/executor/execExpr.h
--- b/src/include/executor/execExpr.h
***************
*** 16,22 ****
#include "nodes/execnodes.h"
! /* forward reference to avoid circularity */
struct ArrayRefState;
/* Bits in ExprState->flags (see also execnodes.h for public flag bits): */
--- 16,23 ----
#include "nodes/execnodes.h"
! /* forward references to avoid circularity */
! struct ExprEvalStep;
struct ArrayRefState;
/* Bits in ExprState->flags (see also execnodes.h for public flag bits): */
*************** struct ArrayRefState;
*** 25,30 ****
--- 26,36 ----
/* jump-threading is in use */
#define EEO_FLAG_DIRECT_THREADED (1 << 2)
+ /* Typical API for out-of-line evaluation subroutines */
+ typedef void (*ExecEvalSubroutine) (ExprState *state,
+ struct ExprEvalStep *op,
+ ExprContext *econtext);
+
/*
* Discriminator for ExprEvalSteps.
*
*************** typedef enum ExprEvalOp
*** 131,136 ****
--- 137,143 ----
/* evaluate PARAM_EXEC/EXTERN parameters */
EEOP_PARAM_EXEC,
EEOP_PARAM_EXTERN,
+ EEOP_PARAM_CALLBACK,
/* return CaseTestExpr value */
EEOP_CASE_TESTVAL,
*************** typedef struct ExprEvalStep
*** 331,336 ****
--- 338,352 ----
Oid paramtype; /* OID of parameter's datatype */
} param;
+ /* for EEOP_PARAM_CALLBACK */
+ struct
+ {
+ ExecEvalSubroutine paramfunc; /* add-on evaluation subroutine */
+ void *paramarg; /* private data for same */
+ int paramid; /* numeric ID for parameter */
+ Oid paramtype; /* OID of parameter's datatype */
+ } cparam;
+
/* for EEOP_CASE_TESTVAL/DOMAIN_TESTVAL */
struct
{
*************** typedef struct ArrayRefState
*** 598,605 ****
} ArrayRefState;
! extern void ExecReadyInterpretedExpr(ExprState *state);
extern ExprEvalOp ExecEvalStepOp(ExprState *state, ExprEvalStep *op);
/*
--- 614,624 ----
} ArrayRefState;
! /* functions in execExpr.c */
! extern void ExprEvalPushStep(ExprState *es, const ExprEvalStep *s);
+ /* functions in execExprInterp.c */
+ extern void ExecReadyInterpretedExpr(ExprState *state);
extern ExprEvalOp ExecEvalStepOp(ExprState *state, ExprEvalStep *op);
/*
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
index dea9216..2cc74da 100644
*** a/src/include/executor/executor.h
--- b/src/include/executor/executor.h
*************** ExecProcNode(PlanState *node)
*** 247,252 ****
--- 247,253 ----
* prototypes from functions in execExpr.c
*/
extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
+ extern ExprState *ExecInitExprWithParams(Expr *node, ParamListInfo ext_params);
extern ExprState *ExecInitQual(List *qual, PlanState *parent);
extern ExprState *ExecInitCheck(List *qual, PlanState *parent);
extern List *ExecInitExprList(List *nodes, PlanState *parent);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 1a35c5c..2b1750c 100644
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
***************
*** 32,37 ****
--- 32,43 ----
#include "storage/condition_variable.h"
+ struct PlanState; /* forward references in this file */
+ struct ExprState;
+ struct ExprContext;
+ struct ExprEvalStep; /* avoid including execExpr.h everywhere */
+
+
/* ----------------
* ExprState node
*
***************
*** 39,48 ****
* It contains instructions (in ->steps) to evaluate the expression.
* ----------------
*/
- struct ExprState; /* forward references in this file */
- struct ExprContext;
- struct ExprEvalStep; /* avoid including execExpr.h everywhere */
-
typedef Datum (*ExprStateEvalFunc) (struct ExprState *expression,
struct ExprContext *econtext,
bool *isNull);
--- 45,50 ----
*************** typedef struct ExprState
*** 84,95 ****
Expr *expr;
/*
! * XXX: following only needed during "compilation", could be thrown away.
*/
int steps_len; /* number of steps currently */
int steps_alloc; /* allocated length of steps array */
Datum *innermost_caseval;
bool *innermost_casenull;
--- 86,101 ----
Expr *expr;
/*
! * XXX: following fields only needed during "compilation" (ExecInitExpr);
! * could be thrown away afterwards.
*/
int steps_len; /* number of steps currently */
int steps_alloc; /* allocated length of steps array */
+ struct PlanState *parent; /* parent PlanState node, if any */
+ ParamListInfo ext_params; /* for compiling PARAM_EXTERN nodes */
+
Datum *innermost_caseval;
bool *innermost_casenull;
*************** typedef struct DomainConstraintState
*** 824,831 ****
* ----------------------------------------------------------------
*/
- struct PlanState;
-
/* ----------------
* ExecProcNodeMtd
*
--- 830,835 ----
diff --git a/src/include/nodes/params.h b/src/include/nodes/params.h
index 55219da..b198db5 100644
*** a/src/include/nodes/params.h
--- b/src/include/nodes/params.h
***************
*** 16,31 ****
/* Forward declarations, to avoid including other headers */
struct Bitmapset;
struct ParseState;
! /* ----------------
* ParamListInfo
*
! * ParamListInfo arrays are used to pass parameters into the executor
! * for parameterized plans. Each entry in the array defines the value
! * to be substituted for a PARAM_EXTERN parameter. The "paramid"
! * of a PARAM_EXTERN Param can range from 1 to numParams.
*
* Although parameter numbers are normally consecutive, we allow
* ptype == InvalidOid to signal an unused array entry.
--- 16,38 ----
/* Forward declarations, to avoid including other headers */
struct Bitmapset;
+ struct ExprState;
+ struct Param;
struct ParseState;
! /*
* ParamListInfo
*
! * ParamListInfo structures are used to pass parameters into the executor
! * for parameterized plans. We support two basic approaches to supplying
! * parameter values, the "static" way and the "dynamic" way.
! *
! * In the static approach, per-parameter data is stored in an array of
! * ParamExternData structs appended to the ParamListInfo struct.
! * Each entry in the array defines the value to be substituted for a
! * PARAM_EXTERN parameter. The "paramid" of a PARAM_EXTERN Param
! * can range from 1 to numParams.
*
* Although parameter numbers are normally consecutive, we allow
* ptype == InvalidOid to signal an unused array entry.
*************** struct ParseState;
*** 35,52 ****
* as a constant (i.e., generate a plan that works only for this value
* of the parameter).
*
! * There are two hook functions that can be associated with a ParamListInfo
! * array to support dynamic parameter handling. First, if paramFetch
! * isn't null and the executor requires a value for an invalid parameter
! * (one with ptype == InvalidOid), the paramFetch hook is called to give
! * it a chance to fill in the parameter value. Second, a parserSetup
! * hook can be supplied to re-instantiate the original parsing hooks if
! * a query needs to be re-parsed/planned (as a substitute for supposing
! * that the current ptype values represent a fixed set of parameter types).
!
* Although the data structure is really an array, not a list, we keep
* the old typedef name to avoid unnecessary code changes.
! * ----------------
*/
#define PARAM_FLAG_CONST 0x0001 /* parameter is constant */
--- 42,88 ----
* as a constant (i.e., generate a plan that works only for this value
* of the parameter).
*
! * In the dynamic approach, all access to parameter values is done through
! * hook functions found in the ParamListInfo struct. In this case,
! * the ParamExternData array is typically unused and not allocated;
! * but the legal range of paramid is still 1 to numParams.
! *
* Although the data structure is really an array, not a list, we keep
* the old typedef name to avoid unnecessary code changes.
! *
! * There are 3 hook functions that can be associated with a ParamListInfo
! * structure:
! *
! * If paramFetch isn't null, it is called to fetch the ParamExternData
! * for a particular param ID, rather than accessing the relevant element
! * of the ParamExternData array. This supports the case where the array
! * isn't there at all, as well as cases where the data in the array
! * might be obsolete or lazily evaluated. paramFetch must return the
! * address of a ParamExternData struct describing the specified param ID;
! * the convention above about ptype == InvalidOid signaling an invalid
! * param ID still applies. The returned struct can either be placed in
! * the "workspace" supplied by the caller, or it can be in storage
! * controlled by the paramFetch hook if that's more convenient.
! * (In either case, the struct is not expected to be long-lived.)
! * If "speculative" is true, the paramFetch hook should not risk errors
! * in trying to fetch the parameter value, and should report an invalid
! * parameter instead.
! *
! * If paramCompile isn't null, then it controls what execExpr.c compiles
! * for PARAM_EXTERN Param nodes --- typically, this hook would emit a
! * EEOP_PARAM_CALLBACK step. This allows unnecessary work to be
! * optimized away in compiled expressions.
! *
! * If parserSetup isn't null, then it is called to re-instantiate the
! * original parsing hooks when a query needs to be re-parsed/planned.
! * This is especially useful if the types of parameters might change
! * from time to time, since it can replace the need to supply a fixed
! * list of parameter types to the parser.
! *
! * Notice that the paramFetch and paramCompile hooks are actually passed
! * the ParamListInfo struct's address; they can therefore access all
! * three of the "arg" fields, and the distinction between paramFetchArg
! * and paramCompileArg is rather arbitrary.
*/
#define PARAM_FLAG_CONST 0x0001 /* parameter is constant */
*************** typedef struct ParamExternData
*** 61,67 ****
typedef struct ParamListInfoData *ParamListInfo;
! typedef void (*ParamFetchHook) (ParamListInfo params, int paramid);
typedef void (*ParserSetupHook) (struct ParseState *pstate, void *arg);
--- 97,109 ----
typedef struct ParamListInfoData *ParamListInfo;
! typedef ParamExternData *(*ParamFetchHook) (ParamListInfo params,
! int paramid, bool speculative,
! ParamExternData *workspace);
!
! typedef void (*ParamCompileHook) (ParamListInfo params, struct Param *param,
! struct ExprState *state,
! Datum *resv, bool *resnull);
typedef void (*ParserSetupHook) (struct ParseState *pstate, void *arg);
*************** typedef struct ParamListInfoData
*** 69,78 ****
{
ParamFetchHook paramFetch; /* parameter fetch hook */
void *paramFetchArg;
ParserSetupHook parserSetup; /* parser setup hook */
void *parserSetupArg;
! int numParams; /* number of ParamExternDatas following */
! struct Bitmapset *paramMask; /* if non-NULL, can ignore omitted params */
ParamExternData params[FLEXIBLE_ARRAY_MEMBER];
} ParamListInfoData;
--- 111,126 ----
{
ParamFetchHook paramFetch; /* parameter fetch hook */
void *paramFetchArg;
+ ParamCompileHook paramCompile; /* parameter compile hook */
+ void *paramCompileArg;
ParserSetupHook parserSetup; /* parser setup hook */
void *parserSetupArg;
! int numParams; /* nominal/maximum # of Params represented */
!
! /*
! * params[] may be of length zero if paramFetch is supplied; otherwise it
! * must be of length numParams.
! */
ParamExternData params[FLEXIBLE_ARRAY_MEMBER];
} ParamListInfoData;
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index 2d7844b..e9eab17 100644
*** a/src/pl/plpgsql/src/pl_comp.c
--- b/src/pl/plpgsql/src/pl_comp.c
*************** plpgsql_adddatum(PLpgSQL_datum *new)
*** 2350,2384 ****
/* ----------
* plpgsql_finish_datums Copy completed datum info into function struct.
- *
- * This is also responsible for building resettable_datums, a bitmapset
- * of the dnos of all ROW, REC, and RECFIELD datums in the function.
* ----------
*/
static void
plpgsql_finish_datums(PLpgSQL_function *function)
{
- Bitmapset *resettable_datums = NULL;
int i;
function->ndatums = plpgsql_nDatums;
function->datums = palloc(sizeof(PLpgSQL_datum *) * plpgsql_nDatums);
for (i = 0; i < plpgsql_nDatums; i++)
- {
function->datums[i] = plpgsql_Datums[i];
- switch (function->datums[i]->dtype)
- {
- case PLPGSQL_DTYPE_ROW:
- case PLPGSQL_DTYPE_REC:
- case PLPGSQL_DTYPE_RECFIELD:
- resettable_datums = bms_add_member(resettable_datums, i);
- break;
-
- default:
- break;
- }
- }
- function->resettable_datums = resettable_datums;
}
--- 2350,2366 ----
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index fa4d573..b8ebd57 100644
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
***************
*** 22,27 ****
--- 22,28 ----
#include "access/tupconvert.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
+ #include "executor/execExpr.h"
#include "executor/spi.h"
#include "funcapi.h"
#include "miscadmin.h"
*************** static int exec_for_query(PLpgSQL_execst
*** 268,276 ****
Portal portal, bool prefetch_ok);
static ParamListInfo setup_param_list(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr);
! static ParamListInfo setup_unshared_param_list(PLpgSQL_execstate *estate,
! PLpgSQL_expr *expr);
! static void plpgsql_param_fetch(ParamListInfo params, int paramid);
static void exec_move_row(PLpgSQL_execstate *estate,
PLpgSQL_variable *target,
HeapTuple tup, TupleDesc tupdesc);
--- 269,286 ----
Portal portal, bool prefetch_ok);
static ParamListInfo setup_param_list(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr);
! static ParamExternData *plpgsql_param_fetch(ParamListInfo params,
! int paramid, bool speculative,
! ParamExternData *workspace);
! static void plpgsql_param_compile(ParamListInfo params, Param *param,
! ExprState *state,
! Datum *resv, bool *resnull);
! static void plpgsql_param_eval_var(ExprState *state, ExprEvalStep *op,
! ExprContext *econtext);
! static void plpgsql_param_eval_var_ro(ExprState *state, ExprEvalStep *op,
! ExprContext *econtext);
! static void plpgsql_param_eval_non_var(ExprState *state, ExprEvalStep *op,
! ExprContext *econtext);
static void exec_move_row(PLpgSQL_execstate *estate,
PLpgSQL_variable *target,
HeapTuple tup, TupleDesc tupdesc);
*************** exec_stmt_forc(PLpgSQL_execstate *estate
*** 2346,2354 ****
exec_prepare_plan(estate, query, curvar->cursor_options);
/*
! * Set up short-lived ParamListInfo
*/
! paramLI = setup_unshared_param_list(estate, query);
/*
* Open the cursor (the paramlist will get copied into the portal)
--- 2356,2364 ----
exec_prepare_plan(estate, query, curvar->cursor_options);
/*
! * Set up ParamListInfo for this query
*/
! paramLI = setup_param_list(estate, query);
/*
* Open the cursor (the paramlist will get copied into the portal)
*************** plpgsql_estate_setup(PLpgSQL_execstate *
*** 3440,3456 ****
estate->datums = palloc(sizeof(PLpgSQL_datum *) * estate->ndatums);
/* caller is expected to fill the datums array */
! /* initialize ParamListInfo with one entry per datum, all invalid */
estate->paramLI = (ParamListInfo)
! palloc0(offsetof(ParamListInfoData, params) +
! estate->ndatums * sizeof(ParamExternData));
estate->paramLI->paramFetch = plpgsql_param_fetch;
estate->paramLI->paramFetchArg = (void *) estate;
estate->paramLI->parserSetup = (ParserSetupHook) plpgsql_parser_setup;
estate->paramLI->parserSetupArg = NULL; /* filled during use */
estate->paramLI->numParams = estate->ndatums;
- estate->paramLI->paramMask = NULL;
- estate->params_dirty = false;
/* set up for use of appropriate simple-expression EState and cast hash */
if (simple_eval_estate)
--- 3450,3465 ----
estate->datums = palloc(sizeof(PLpgSQL_datum *) * estate->ndatums);
/* caller is expected to fill the datums array */
! /* initialize our ParamListInfo with appropriate hook functions */
estate->paramLI = (ParamListInfo)
! palloc(offsetof(ParamListInfoData, params));
estate->paramLI->paramFetch = plpgsql_param_fetch;
estate->paramLI->paramFetchArg = (void *) estate;
+ estate->paramLI->paramCompile = plpgsql_param_compile;
+ estate->paramLI->paramCompileArg = NULL; /* not needed */
estate->paramLI->parserSetup = (ParserSetupHook) plpgsql_parser_setup;
estate->paramLI->parserSetupArg = NULL; /* filled during use */
estate->paramLI->numParams = estate->ndatums;
/* set up for use of appropriate simple-expression EState and cast hash */
if (simple_eval_estate)
*************** exec_stmt_open(PLpgSQL_execstate *estate
*** 4169,4180 ****
}
/*
! * Set up short-lived ParamListInfo
*/
! paramLI = setup_unshared_param_list(estate, query);
/*
! * Open the cursor
*/
portal = SPI_cursor_open_with_paramlist(curname, query->plan,
paramLI,
--- 4178,4189 ----
}
/*
! * Set up ParamListInfo for this query
*/
! paramLI = setup_param_list(estate, query);
/*
! * Open the cursor (the paramlist will get copied into the portal)
*/
portal = SPI_cursor_open_with_paramlist(curname, query->plan,
paramLI,
*************** exec_run_select(PLpgSQL_execstate *estat
*** 5268,5282 ****
portalP == NULL ? CURSOR_OPT_PARALLEL_OK : 0);
/*
! * If a portal was requested, put the query into the portal
*/
if (portalP != NULL)
{
- /*
- * Set up short-lived ParamListInfo
- */
- paramLI = setup_unshared_param_list(estate, expr);
-
*portalP = SPI_cursor_open_with_paramlist(NULL, expr->plan,
paramLI,
estate->readonly_func);
--- 5277,5291 ----
portalP == NULL ? CURSOR_OPT_PARALLEL_OK : 0);
/*
! * Set up ParamListInfo to pass to executor
! */
! paramLI = setup_param_list(estate, expr);
!
! /*
! * If a portal was requested, put the query and paramlist into the portal
*/
if (portalP != NULL)
{
*portalP = SPI_cursor_open_with_paramlist(NULL, expr->plan,
paramLI,
estate->readonly_func);
*************** exec_run_select(PLpgSQL_execstate *estat
*** 5288,5298 ****
}
/*
- * Set up ParamListInfo to pass to executor
- */
- paramLI = setup_param_list(estate, expr);
-
- /*
* Execute the query
*/
rc = SPI_execute_plan_with_paramlist(expr->plan, paramLI,
--- 5297,5302 ----
*************** exec_eval_simple_expr(PLpgSQL_execstate
*** 5504,5510 ****
ExprContext *econtext = estate->eval_econtext;
LocalTransactionId curlxid = MyProc->lxid;
CachedPlan *cplan;
- ParamListInfo paramLI;
void *save_setup_arg;
MemoryContext oldcontext;
--- 5508,5513 ----
*************** exec_eval_simple_expr(PLpgSQL_execstate
*** 5552,5557 ****
--- 5555,5568 ----
*rettypmod = expr->expr_simple_typmod;
/*
+ * Set up ParamListInfo to pass to executor. For safety, save and restore
+ * estate->paramLI->parserSetupArg around our use of the param list.
+ */
+ save_setup_arg = estate->paramLI->parserSetupArg;
+
+ econtext->ecxt_param_list_info = setup_param_list(estate, expr);
+
+ /*
* Prepare the expression for execution, if it's not been done already in
* the current transaction. (This will be forced to happen if we called
* exec_save_simple_expr above.)
*************** exec_eval_simple_expr(PLpgSQL_execstate
*** 5559,5565 ****
if (expr->expr_simple_lxid != curlxid)
{
oldcontext = MemoryContextSwitchTo(estate->simple_eval_estate->es_query_cxt);
! expr->expr_simple_state = ExecInitExpr(expr->expr_simple_expr, NULL);
expr->expr_simple_in_use = false;
expr->expr_simple_lxid = curlxid;
MemoryContextSwitchTo(oldcontext);
--- 5570,5578 ----
if (expr->expr_simple_lxid != curlxid)
{
oldcontext = MemoryContextSwitchTo(estate->simple_eval_estate->es_query_cxt);
! expr->expr_simple_state =
! ExecInitExprWithParams(expr->expr_simple_expr,
! econtext->ecxt_param_list_info);
expr->expr_simple_in_use = false;
expr->expr_simple_lxid = curlxid;
MemoryContextSwitchTo(oldcontext);
*************** exec_eval_simple_expr(PLpgSQL_execstate
*** 5579,5599 ****
}
/*
- * Set up ParamListInfo to pass to executor. We need an unshared list if
- * it's going to include any R/W expanded-object pointer. For safety,
- * save and restore estate->paramLI->parserSetupArg around our use of the
- * param list.
- */
- save_setup_arg = estate->paramLI->parserSetupArg;
-
- if (expr->rwparam >= 0)
- paramLI = setup_unshared_param_list(estate, expr);
- else
- paramLI = setup_param_list(estate, expr);
-
- econtext->ecxt_param_list_info = paramLI;
-
- /*
* Mark expression as busy for the duration of the ExecEvalExpr call.
*/
expr->expr_simple_in_use = true;
--- 5592,5597 ----
*************** exec_eval_simple_expr(PLpgSQL_execstate
*** 5632,5666 ****
/*
* Create a ParamListInfo to pass to SPI
*
! * We share a single ParamListInfo array across all SPI calls made from this
! * estate, except calls creating cursors, which use setup_unshared_param_list
! * (see its comments for reasons why), and calls that pass a R/W expanded
! * object pointer. A shared array is generally OK since any given slot in
! * the array would need to contain the same current datum value no matter
! * which query or expression we're evaluating; but of course that doesn't
! * hold when a specific variable is being passed as a R/W pointer, because
! * other expressions in the same function probably don't want to do that.
! *
! * Note that paramLI->parserSetupArg points to the specific PLpgSQL_expr
! * being evaluated. This is not an issue for statement-level callers, but
! * lower-level callers must save and restore estate->paramLI->parserSetupArg
! * just in case there's an active evaluation at an outer call level.
*
! * The general plan for passing parameters to SPI is that plain VAR datums
! * always have valid images in the shared param list. This is ensured by
! * assign_simple_var(), which also marks those params as PARAM_FLAG_CONST,
! * allowing the planner to use those values in custom plans. However, non-VAR
! * datums cannot conveniently be managed that way. For one thing, they could
! * throw errors (for example "no such record field") and we do not want that
! * to happen in a part of the expression that might never be evaluated at
! * runtime. For another thing, exec_eval_datum() may return short-lived
! * values stored in the estate's eval_mcontext, which will not necessarily
! * survive to the next SPI operation. And for a third thing, ROW
! * and RECFIELD datums' values depend on other datums, and we don't have a
! * cheap way to track that. Therefore, param slots for non-VAR datum types
! * are always reset here and then filled on-demand by plpgsql_param_fetch().
! * We can save a few cycles by not bothering with the reset loop unless at
! * least one such param has actually been filled by plpgsql_param_fetch().
*/
static ParamListInfo
setup_param_list(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
--- 5630,5646 ----
/*
* Create a ParamListInfo to pass to SPI
*
! * We use a single ParamListInfo struct for all SPI calls made from this
! * estate; it contains no per-param data, just hook functions, so it's
! * effectively read-only for SPI.
*
! * An exception from pure read-only-ness is that the parserSetupArg points
! * to the specific PLpgSQL_expr being evaluated. This is not an issue for
! * statement-level callers, but lower-level callers must save and restore
! * estate->paramLI->parserSetupArg just in case there's an active evaluation
! * at an outer call level. (A plausible alternative design would be to
! * create a ParamListInfo struct for each PLpgSQL_expr, but for the moment
! * that seems like a waste of memory.)
*/
static ParamListInfo
setup_param_list(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
*************** setup_param_list(PLpgSQL_execstate *esta
*** 5674,5684 ****
Assert(expr->plan != NULL);
/*
- * Expressions with R/W parameters can't use the shared param list.
- */
- Assert(expr->rwparam == -1);
-
- /*
* We only need a ParamListInfo if the expression has parameters. In
* principle we should test with bms_is_empty(), but we use a not-null
* test because it's faster. In current usage bits are never removed from
--- 5654,5659 ----
*************** setup_param_list(PLpgSQL_execstate *esta
*** 5690,5714 ****
paramLI = estate->paramLI;
/*
- * If any resettable parameters have been passed to the executor since
- * last time, we need to reset those param slots to "invalid", for the
- * reasons mentioned in the comment above.
- */
- if (estate->params_dirty)
- {
- Bitmapset *resettable_datums = estate->func->resettable_datums;
- int dno = -1;
-
- while ((dno = bms_next_member(resettable_datums, dno)) >= 0)
- {
- ParamExternData *prm = ¶mLI->params[dno];
-
- prm->ptype = InvalidOid;
- }
- estate->params_dirty = false;
- }
-
- /*
* Set up link to active expr where the hook functions can find it.
* Callers must save and restore parserSetupArg if there is any chance
* that they are interrupting an active use of parameters.
--- 5665,5670 ----
*************** setup_param_list(PLpgSQL_execstate *esta
*** 5716,5727 ****
paramLI->parserSetupArg = (void *) expr;
/*
- * Allow parameters that aren't needed by this expression to be
- * ignored.
- */
- paramLI->paramMask = expr->paramnos;
-
- /*
* Also make sure this is set before parser hooks need it. There is
* no need to save and restore, since the value is always correct once
* set. (Should be set already, but let's be sure.)
--- 5672,5677 ----
*************** setup_param_list(PLpgSQL_execstate *esta
*** 5741,5855 ****
}
/*
! * Create an unshared, short-lived ParamListInfo to pass to SPI
! *
! * When creating a cursor, we do not use the shared ParamListInfo array
! * but create a short-lived one that will contain only params actually
! * referenced by the query. The reason for this is that copyParamList() will
! * be used to copy the parameters into cursor-lifespan storage, and we don't
! * want it to copy anything that's not used by the specific cursor; that
! * could result in uselessly copying some large values.
! *
! * We also use this for expressions that are passing a R/W object pointer
! * to some trusted function. We don't want the R/W pointer to get into the
! * shared param list, where it could get passed to some less-trusted function.
*
! * The result, if not NULL, is in the estate's eval_mcontext.
*
! * XXX. Could we use ParamListInfo's new paramMask to avoid creating unshared
! * parameter lists?
! */
! static ParamListInfo
! setup_unshared_param_list(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
! {
! ParamListInfo paramLI;
!
! /*
! * We must have created the SPIPlan already (hence, query text has been
! * parsed/analyzed at least once); else we cannot rely on expr->paramnos.
! */
! Assert(expr->plan != NULL);
!
! /*
! * We only need a ParamListInfo if the expression has parameters. In
! * principle we should test with bms_is_empty(), but we use a not-null
! * test because it's faster. In current usage bits are never removed from
! * expr->paramnos, only added, so this test is correct anyway.
! */
! if (expr->paramnos)
! {
! int dno;
!
! /* initialize ParamListInfo with one entry per datum, all invalid */
! paramLI = (ParamListInfo)
! eval_mcontext_alloc0(estate,
! offsetof(ParamListInfoData, params) +
! estate->ndatums * sizeof(ParamExternData));
! paramLI->paramFetch = plpgsql_param_fetch;
! paramLI->paramFetchArg = (void *) estate;
! paramLI->parserSetup = (ParserSetupHook) plpgsql_parser_setup;
! paramLI->parserSetupArg = (void *) expr;
! paramLI->numParams = estate->ndatums;
! paramLI->paramMask = NULL;
!
! /*
! * Instantiate values for "safe" parameters of the expression. We
! * could skip this and leave them to be filled by plpgsql_param_fetch;
! * but then the values would not be available for query planning,
! * since the planner doesn't call the paramFetch hook.
! */
! dno = -1;
! while ((dno = bms_next_member(expr->paramnos, dno)) >= 0)
! {
! PLpgSQL_datum *datum = estate->datums[dno];
!
! if (datum->dtype == PLPGSQL_DTYPE_VAR)
! {
! PLpgSQL_var *var = (PLpgSQL_var *) datum;
! ParamExternData *prm = ¶mLI->params[dno];
!
! if (dno == expr->rwparam)
! prm->value = var->value;
! else
! prm->value = MakeExpandedObjectReadOnly(var->value,
! var->isnull,
! var->datatype->typlen);
! prm->isnull = var->isnull;
! prm->pflags = PARAM_FLAG_CONST;
! prm->ptype = var->datatype->typoid;
! }
! }
!
! /*
! * Also make sure this is set before parser hooks need it. There is
! * no need to save and restore, since the value is always correct once
! * set. (Should be set already, but let's be sure.)
! */
! expr->func = estate->func;
! }
! else
! {
! /*
! * Expression requires no parameters. Be sure we represent this case
! * as a NULL ParamListInfo, so that plancache.c knows there is no
! * point in a custom plan.
! */
! paramLI = NULL;
! }
! return paramLI;
! }
!
! /*
! * plpgsql_param_fetch paramFetch callback for dynamic parameter fetch
*/
! static void
! plpgsql_param_fetch(ParamListInfo params, int paramid)
{
int dno;
PLpgSQL_execstate *estate;
PLpgSQL_expr *expr;
PLpgSQL_datum *datum;
! ParamExternData *prm;
int32 prmtypmod;
/* paramid's are 1-based, but dnos are 0-based */
--- 5691,5715 ----
}
/*
! * plpgsql_param_fetch paramFetch callback for dynamic parameter fetch
*
! * We always use the caller's workspace to construct the returned struct.
*
! * Note: this is no longer used during query execution. It is used during
! * planning (with speculative == true) and when the ParamListInfo we supply
! * to the executor is copied into a cursor portal or transferred to a
! * parallel child process.
*/
! static ParamExternData *
! plpgsql_param_fetch(ParamListInfo params,
! int paramid, bool speculative,
! ParamExternData *prm)
{
int dno;
PLpgSQL_execstate *estate;
PLpgSQL_expr *expr;
PLpgSQL_datum *datum;
! bool ok = true;
int32 prmtypmod;
/* paramid's are 1-based, but dnos are 0-based */
*************** plpgsql_param_fetch(ParamListInfo params
*** 5866,5900 ****
/*
* Since copyParamList() or SerializeParamList() will try to materialize
! * every single parameter slot, it's important to do nothing when asked
! * for a datum that's not supposed to be used by this SQL expression.
! * Otherwise we risk failures in exec_eval_datum(), or copying a lot more
! * data than necessary.
*/
if (!bms_is_member(dno, expr->paramnos))
! return;
! if (params == estate->paramLI)
{
- /*
- * We need to mark the shared params array dirty if we're about to
- * evaluate a resettable datum.
- */
switch (datum->dtype)
{
case PLPGSQL_DTYPE_ROW:
case PLPGSQL_DTYPE_REC:
case PLPGSQL_DTYPE_RECFIELD:
! estate->params_dirty = true;
! break;
default:
break;
}
}
! /* OK, evaluate the value and store into the appropriate paramlist slot */
! prm = ¶ms->params[dno];
exec_eval_datum(estate, datum,
&prm->ptype, &prmtypmod,
&prm->value, &prm->isnull);
--- 5726,5799 ----
/*
* Since copyParamList() or SerializeParamList() will try to materialize
! * every single parameter slot, it's important to return a dummy param
! * when asked for a datum that's not supposed to be used by this SQL
! * expression. Otherwise we risk failures in exec_eval_datum(), or
! * copying a lot more data than necessary.
*/
if (!bms_is_member(dno, expr->paramnos))
! ok = false;
! /*
! * If the access is speculative, we prefer to return no data rather than
! * to fail in exec_eval_datum(). Check the likely failure cases.
! */
! else if (speculative)
{
switch (datum->dtype)
{
+ case PLPGSQL_DTYPE_VAR:
+ /* always safe */
+ break;
+
case PLPGSQL_DTYPE_ROW:
+ /* should be safe in all interesting cases */
+ break;
+
case PLPGSQL_DTYPE_REC:
+ {
+ PLpgSQL_rec *rec = (PLpgSQL_rec *) datum;
+
+ if (!HeapTupleIsValid(rec->tup))
+ ok = false;
+ break;
+ }
+
case PLPGSQL_DTYPE_RECFIELD:
! {
! PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) datum;
! PLpgSQL_rec *rec;
! int fno;
!
! rec = (PLpgSQL_rec *) (estate->datums[recfield->recparentno]);
! if (!HeapTupleIsValid(rec->tup))
! ok = false;
! else
! {
! fno = SPI_fnumber(rec->tupdesc, recfield->fieldname);
! if (fno == SPI_ERROR_NOATTRIBUTE)
! ok = false;
! }
! break;
! }
default:
+ ok = false;
break;
}
}
! /* Return "no such parameter" if not ok */
! if (!ok)
! {
! prm->value = (Datum) 0;
! prm->isnull = true;
! prm->pflags = 0;
! prm->ptype = InvalidOid;
! return prm;
! }
!
! /* OK, evaluate the value and store into the return struct */
exec_eval_datum(estate, datum,
&prm->ptype, &prmtypmod,
&prm->value, &prm->isnull);
*************** plpgsql_param_fetch(ParamListInfo params
*** 5909,5914 ****
--- 5808,5981 ----
prm->value = MakeExpandedObjectReadOnly(prm->value,
prm->isnull,
((PLpgSQL_var *) datum)->datatype->typlen);
+
+ return prm;
+ }
+
+ /*
+ * plpgsql_param_compile paramCompile callback for plpgsql parameters
+ */
+ static void
+ plpgsql_param_compile(ParamListInfo params, Param *param,
+ ExprState *state,
+ Datum *resv, bool *resnull)
+ {
+ PLpgSQL_execstate *estate;
+ PLpgSQL_expr *expr;
+ int dno;
+ PLpgSQL_datum *datum;
+ ExprEvalStep scratch;
+
+ /* fetch back the hook data */
+ estate = (PLpgSQL_execstate *) params->paramFetchArg;
+ expr = (PLpgSQL_expr *) params->parserSetupArg;
+
+ /* paramid's are 1-based, but dnos are 0-based */
+ dno = param->paramid - 1;
+ Assert(dno >= 0 && dno < estate->ndatums);
+
+ /* now we can access the target datum */
+ datum = estate->datums[dno];
+
+ scratch.opcode = EEOP_PARAM_CALLBACK;
+ scratch.resvalue = resv;
+ scratch.resnull = resnull;
+
+ /* Select appropriate eval function */
+ if (datum->dtype == PLPGSQL_DTYPE_VAR)
+ {
+ if (dno != expr->rwparam &&
+ ((PLpgSQL_var *) datum)->datatype->typlen == -1)
+ scratch.d.cparam.paramfunc = plpgsql_param_eval_var_ro;
+ else
+ scratch.d.cparam.paramfunc = plpgsql_param_eval_var;
+ }
+ else
+ scratch.d.cparam.paramfunc = plpgsql_param_eval_non_var;
+
+ /*
+ * Note: it's tempting to use paramarg to store the estate pointer and
+ * thereby save an indirection or two in the eval functions. But that
+ * doesn't work because the compiled expression might be used with
+ * different estates for the same PL/pgSQL function.
+ */
+ scratch.d.cparam.paramarg = NULL;
+ scratch.d.cparam.paramid = param->paramid;
+ scratch.d.cparam.paramtype = param->paramtype;
+ ExprEvalPushStep(state, &scratch);
+ }
+
+ /*
+ * plpgsql_param_eval_var evaluation of EEOP_PARAM_CALLBACK step
+ *
+ * This is specialized to the case of DTYPE_VAR variables for which
+ * we do not need to invoke MakeExpandedObjectReadOnly.
+ */
+ static void
+ plpgsql_param_eval_var(ExprState *state, ExprEvalStep *op,
+ ExprContext *econtext)
+ {
+ ParamListInfo params;
+ PLpgSQL_execstate *estate;
+ int dno = op->d.cparam.paramid - 1;
+ PLpgSQL_var *var;
+
+ /* fetch back the hook data */
+ params = econtext->ecxt_param_list_info;
+ estate = (PLpgSQL_execstate *) params->paramFetchArg;
+ Assert(dno >= 0 && dno < estate->ndatums);
+
+ /* now we can access the target datum */
+ var = (PLpgSQL_var *) estate->datums[dno];
+ Assert(var->dtype == PLPGSQL_DTYPE_VAR);
+
+ /* inlined version of exec_eval_datum() */
+ *op->resvalue = var->value;
+ *op->resnull = var->isnull;
+
+ /* safety check -- an assertion should be sufficient */
+ Assert(var->datatype->typoid == op->d.cparam.paramtype);
+ }
+
+ /*
+ * plpgsql_param_eval_var_ro evaluation of EEOP_PARAM_CALLBACK step
+ *
+ * This is specialized to the case of DTYPE_VAR variables for which
+ * we need to invoke MakeExpandedObjectReadOnly.
+ */
+ static void
+ plpgsql_param_eval_var_ro(ExprState *state, ExprEvalStep *op,
+ ExprContext *econtext)
+ {
+ ParamListInfo params;
+ PLpgSQL_execstate *estate;
+ int dno = op->d.cparam.paramid - 1;
+ PLpgSQL_var *var;
+
+ /* fetch back the hook data */
+ params = econtext->ecxt_param_list_info;
+ estate = (PLpgSQL_execstate *) params->paramFetchArg;
+ Assert(dno >= 0 && dno < estate->ndatums);
+
+ /* now we can access the target datum */
+ var = (PLpgSQL_var *) estate->datums[dno];
+ Assert(var->dtype == PLPGSQL_DTYPE_VAR);
+
+ /*
+ * Inlined version of exec_eval_datum() ... and while we're at it, force
+ * expanded datums to read-only.
+ */
+ *op->resvalue = MakeExpandedObjectReadOnly(var->value,
+ var->isnull,
+ -1);
+ *op->resnull = var->isnull;
+
+ /* safety check -- an assertion should be sufficient */
+ Assert(var->datatype->typoid == op->d.cparam.paramtype);
+ }
+
+ /*
+ * plpgsql_param_eval_non_var evaluation of EEOP_PARAM_CALLBACK step
+ *
+ * This handles all variable types except DTYPE_VAR.
+ */
+ static void
+ plpgsql_param_eval_non_var(ExprState *state, ExprEvalStep *op,
+ ExprContext *econtext)
+ {
+ ParamListInfo params;
+ PLpgSQL_execstate *estate;
+ int dno = op->d.cparam.paramid - 1;
+ PLpgSQL_datum *datum;
+ Oid datumtype;
+ int32 datumtypmod;
+
+ /* fetch back the hook data */
+ params = econtext->ecxt_param_list_info;
+ estate = (PLpgSQL_execstate *) params->paramFetchArg;
+ Assert(dno >= 0 && dno < estate->ndatums);
+
+ /* now we can access the target datum */
+ datum = estate->datums[dno];
+ Assert(datum->dtype != PLPGSQL_DTYPE_VAR);
+
+ exec_eval_datum(estate, datum,
+ &datumtype, &datumtypmod,
+ op->resvalue, op->resnull);
+
+ /* safety check -- needed for, eg, record fields */
+ if (unlikely(datumtype != op->d.cparam.paramtype))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
+ op->d.cparam.paramid,
+ format_type_be(datumtype),
+ format_type_be(op->d.cparam.paramtype))));
+
+ /*
+ * Currently, if the dtype isn't VAR, the value couldn't be a read/write
+ * expanded datum.
+ */
}
*************** plpgsql_subxact_cb(SubXactEvent event, S
*** 6875,6888 ****
* assign_simple_var --- assign a new value to any VAR datum.
*
* This should be the only mechanism for assignment to simple variables,
! * lest we forget to update the paramLI image.
*/
static void
assign_simple_var(PLpgSQL_execstate *estate, PLpgSQL_var *var,
Datum newvalue, bool isnull, bool freeable)
{
- ParamExternData *prm;
-
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
/* Free the old value if needed */
if (var->freeval)
--- 6942,6953 ----
* assign_simple_var --- assign a new value to any VAR datum.
*
* This should be the only mechanism for assignment to simple variables,
! * lest we do the release of the old value incorrectly.
*/
static void
assign_simple_var(PLpgSQL_execstate *estate, PLpgSQL_var *var,
Datum newvalue, bool isnull, bool freeable)
{
Assert(var->dtype == PLPGSQL_DTYPE_VAR);
/* Free the old value if needed */
if (var->freeval)
*************** assign_simple_var(PLpgSQL_execstate *est
*** 6898,6912 ****
var->value = newvalue;
var->isnull = isnull;
var->freeval = freeable;
- /* And update the image in the common parameter list */
- prm = &estate->paramLI->params[var->dno];
- prm->value = MakeExpandedObjectReadOnly(newvalue,
- isnull,
- var->datatype->typlen);
- prm->isnull = isnull;
- /* these might be set already, but let's be sure */
- prm->pflags = PARAM_FLAG_CONST;
- prm->ptype = var->datatype->typoid;
}
/*
--- 6963,6968 ----
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 39bd82a..43d7d7d 100644
*** a/src/pl/plpgsql/src/plpgsql.h
--- b/src/pl/plpgsql/src/plpgsql.h
*************** typedef struct PLpgSQL_function
*** 857,863 ****
/* the datums representing the function's local variables */
int ndatums;
PLpgSQL_datum **datums;
- Bitmapset *resettable_datums; /* dnos of non-simple vars */
/* function body parsetree */
PLpgSQL_stmt_block *action;
--- 857,862 ----
*************** typedef struct PLpgSQL_execstate
*** 899,907 ****
int ndatums;
PLpgSQL_datum **datums;
! /* we pass datums[i] to the executor, when needed, in paramLI->params[i] */
ParamListInfo paramLI;
- bool params_dirty; /* T if any resettable datum has been passed */
/* EState to use for "simple" expression evaluation */
EState *simple_eval_estate;
--- 898,910 ----
int ndatums;
PLpgSQL_datum **datums;
! /*
! * paramLI is what we use to pass local variable values to the executor.
! * It does not have a ParamExternData array; we just dynamically
! * instantiate parameter data as needed. By convention, PARAM_EXTERN
! * Params have paramid equal to the dno of the referenced local variable.
! */
ParamListInfo paramLI;
/* EState to use for "simple" expression evaluation */
EState *simple_eval_estate;