Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit a7f107d

Browse files
committed
Evaluate arguments of correlated SubPlans in the referencing ExprState
Until now we generated an ExprState for each parameter to a SubPlan and evaluated them one-by-one ExecScanSubPlan. That's sub-optimal as creating lots of small ExprStates a) makes JIT compilation more expensive b) wastes memory c) is a bit slower to execute This commit arranges to evaluate parameters to a SubPlan as part of the ExprState referencing a SubPlan, using the new EEOP_PARAM_SET expression step. We emit one EEOP_PARAM_SET for each argument to a subplan, just before the EEOP_SUBPLAN step. It likely is worth using EEOP_PARAM_SET in other places as well, e.g. for SubPlan outputs, nestloop parameters and - more ambitiously - to get rid of ExprContext->domainValue/caseValue/ecxt_agg*. But that's for later. Author: Andres Freund <andres@anarazel.de> Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us> Reviewed-by: Alena Rybakina <lena.ribackina@yandex.ru> Discussion: https://postgr.es/m/20230225214401.346ancgjqc3zmvek@awork3.anarazel.de
1 parent e6a9637 commit a7f107d

File tree

8 files changed

+125
-52
lines changed

8 files changed

+125
-52
lines changed

src/backend/executor/execExpr.c

Lines changed: 70 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,9 @@ static void ExecInitExprRec(Expr *node, ExprState *state,
6969
static void ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args,
7070
Oid funcid, Oid inputcollid,
7171
ExprState *state);
72+
static void ExecInitSubPlanExpr(SubPlan *subplan,
73+
ExprState *state,
74+
Datum *resv, bool *resnull);
7275
static void ExecCreateExprSetupSteps(ExprState *state, Node *node);
7376
static void ExecPushExprSetupSteps(ExprState *state, ExprSetupInfo *info);
7477
static bool expr_setup_walker(Node *node, ExprSetupInfo *info);
@@ -1406,7 +1409,6 @@ ExecInitExprRec(Expr *node, ExprState *state,
14061409
case T_SubPlan:
14071410
{
14081411
SubPlan *subplan = (SubPlan *) node;
1409-
SubPlanState *sstate;
14101412

14111413
/*
14121414
* Real execution of a MULTIEXPR SubPlan has already been
@@ -1423,19 +1425,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
14231425
break;
14241426
}
14251427

1426-
if (!state->parent)
1427-
elog(ERROR, "SubPlan found with no parent plan");
1428-
1429-
sstate = ExecInitSubPlan(subplan, state->parent);
1430-
1431-
/* add SubPlanState nodes to state->parent->subPlan */
1432-
state->parent->subPlan = lappend(state->parent->subPlan,
1433-
sstate);
1434-
1435-
scratch.opcode = EEOP_SUBPLAN;
1436-
scratch.d.subplan.sstate = sstate;
1437-
1438-
ExprEvalPushStep(state, &scratch);
1428+
ExecInitSubPlanExpr(subplan, state, resv, resnull);
14391429
break;
14401430
}
14411431

@@ -2715,6 +2705,70 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
27152705
}
27162706
}
27172707

2708+
/*
2709+
* Append the steps necessary for the evaluation of a SubPlan node to
2710+
* ExprState->steps.
2711+
*
2712+
* subplan - SubPlan expression to evaluate
2713+
* state - ExprState to whose ->steps to append the necessary operations
2714+
* resv / resnull - where to store the result of the node into
2715+
*/
2716+
static void
2717+
ExecInitSubPlanExpr(SubPlan *subplan,
2718+
ExprState *state,
2719+
Datum *resv, bool *resnull)
2720+
{
2721+
ExprEvalStep scratch = {0};
2722+
SubPlanState *sstate;
2723+
ListCell *pvar;
2724+
ListCell *l;
2725+
2726+
if (!state->parent)
2727+
elog(ERROR, "SubPlan found with no parent plan");
2728+
2729+
/*
2730+
* Generate steps to evaluate input arguments for the subplan.
2731+
*
2732+
* We evaluate the argument expressions into ExprState's resvalue/resnull,
2733+
* and then use PARAM_SET to update the parameter. We do that, instead of
2734+
* evaluating directly into the param, to avoid depending on the pointer
2735+
* value remaining stable / being included in the generated expression. No
2736+
* danger of conflicts with other uses of resvalue/resnull as storing and
2737+
* using the value always is in subsequent steps.
2738+
*
2739+
* Any calculation we have to do can be done in the parent econtext, since
2740+
* the Param values don't need to have per-query lifetime.
2741+
*/
2742+
Assert(list_length(subplan->parParam) == list_length(subplan->args));
2743+
forboth(l, subplan->parParam, pvar, subplan->args)
2744+
{
2745+
int paramid = lfirst_int(l);
2746+
Expr *arg = (Expr *) lfirst(pvar);
2747+
2748+
ExecInitExprRec(arg, state,
2749+
&state->resvalue, &state->resnull);
2750+
2751+
scratch.opcode = EEOP_PARAM_SET;
2752+
scratch.d.param.paramid = paramid;
2753+
/* paramtype's not actually used, but we might as well fill it */
2754+
scratch.d.param.paramtype = exprType((Node *) arg);
2755+
ExprEvalPushStep(state, &scratch);
2756+
}
2757+
2758+
sstate = ExecInitSubPlan(subplan, state->parent);
2759+
2760+
/* add SubPlanState nodes to state->parent->subPlan */
2761+
state->parent->subPlan = lappend(state->parent->subPlan,
2762+
sstate);
2763+
2764+
scratch.opcode = EEOP_SUBPLAN;
2765+
scratch.resvalue = resv;
2766+
scratch.resnull = resnull;
2767+
scratch.d.subplan.sstate = sstate;
2768+
2769+
ExprEvalPushStep(state, &scratch);
2770+
}
2771+
27182772
/*
27192773
* Add expression steps performing setup that's needed before any of the
27202774
* main execution of the expression.
@@ -2789,29 +2843,12 @@ ExecPushExprSetupSteps(ExprState *state, ExprSetupInfo *info)
27892843
foreach(lc, info->multiexpr_subplans)
27902844
{
27912845
SubPlan *subplan = (SubPlan *) lfirst(lc);
2792-
SubPlanState *sstate;
27932846

27942847
Assert(subplan->subLinkType == MULTIEXPR_SUBLINK);
27952848

2796-
/* This should match what ExecInitExprRec does for other SubPlans: */
2797-
2798-
if (!state->parent)
2799-
elog(ERROR, "SubPlan found with no parent plan");
2800-
2801-
sstate = ExecInitSubPlan(subplan, state->parent);
2802-
2803-
/* add SubPlanState nodes to state->parent->subPlan */
2804-
state->parent->subPlan = lappend(state->parent->subPlan,
2805-
sstate);
2806-
2807-
scratch.opcode = EEOP_SUBPLAN;
2808-
scratch.d.subplan.sstate = sstate;
2809-
28102849
/* The result can be ignored, but we better put it somewhere */
2811-
scratch.resvalue = &state->resvalue;
2812-
scratch.resnull = &state->resnull;
2813-
2814-
ExprEvalPushStep(state, &scratch);
2850+
ExecInitSubPlanExpr(subplan, state,
2851+
&state->resvalue, &state->resnull);
28152852
}
28162853
}
28172854

src/backend/executor/execExprInterp.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,7 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
450450
&&CASE_EEOP_PARAM_EXEC,
451451
&&CASE_EEOP_PARAM_EXTERN,
452452
&&CASE_EEOP_PARAM_CALLBACK,
453+
&&CASE_EEOP_PARAM_SET,
453454
&&CASE_EEOP_CASE_TESTVAL,
454455
&&CASE_EEOP_MAKE_READONLY,
455456
&&CASE_EEOP_IOCOERCE,
@@ -1093,6 +1094,13 @@ ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
10931094
EEO_NEXT();
10941095
}
10951096

1097+
EEO_CASE(EEOP_PARAM_SET)
1098+
{
1099+
/* out of line, unlikely to matter performancewise */
1100+
ExecEvalParamSet(state, op, econtext);
1101+
EEO_NEXT();
1102+
}
1103+
10961104
EEO_CASE(EEOP_CASE_TESTVAL)
10971105
{
10981106
/*
@@ -2555,6 +2563,24 @@ ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
25552563
errmsg("no value found for parameter %d", paramId)));
25562564
}
25572565

2566+
/*
2567+
* Set value of a param (currently always PARAM_EXEC) from
2568+
* state->res{value,null}.
2569+
*/
2570+
void
2571+
ExecEvalParamSet(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
2572+
{
2573+
ParamExecData *prm;
2574+
2575+
prm = &(econtext->ecxt_param_exec_vals[op->d.param.paramid]);
2576+
2577+
/* Shouldn't have a pending evaluation anymore */
2578+
Assert(prm->execPlan == NULL);
2579+
2580+
prm->value = state->resvalue;
2581+
prm->isnull = state->resnull;
2582+
}
2583+
25582584
/*
25592585
* Evaluate a CoerceViaIO node in soft-error mode.
25602586
*

src/backend/executor/execProcnode.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,10 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
393393
/*
394394
* Initialize any initPlans present in this node. The planner put them in
395395
* a separate list for us.
396+
*
397+
* The defining characteristic of initplans is that they don't have
398+
* arguments, so we don't need to evaluate them (in contrast to
399+
* ExecInitSubPlanExpr()).
396400
*/
397401
subps = NIL;
398402
foreach(l, node->initPlan)
@@ -401,6 +405,7 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
401405
SubPlanState *sstate;
402406

403407
Assert(IsA(subplan, SubPlan));
408+
Assert(subplan->args == NIL);
404409
sstate = ExecInitSubPlan(subplan, result);
405410
subps = lappend(subps, sstate);
406411
}

src/backend/executor/nodeSubplan.c

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ ExecHashSubPlan(SubPlanState *node,
107107
TupleTableSlot *slot;
108108

109109
/* Shouldn't have any direct correlation Vars */
110-
if (subplan->parParam != NIL || node->args != NIL)
110+
if (subplan->parParam != NIL || subplan->args != NIL)
111111
elog(ERROR, "hashed subplan with direct correlation not supported");
112112

113113
/*
@@ -231,7 +231,6 @@ ExecScanSubPlan(SubPlanState *node,
231231
TupleTableSlot *slot;
232232
Datum result;
233233
bool found = false; /* true if got at least one subplan tuple */
234-
ListCell *pvar;
235234
ListCell *l;
236235
ArrayBuildStateAny *astate = NULL;
237236

@@ -248,26 +247,19 @@ ExecScanSubPlan(SubPlanState *node,
248247
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
249248

250249
/*
251-
* Set Params of this plan from parent plan correlation values. (Any
252-
* calculation we have to do is done in the parent econtext, since the
253-
* Param values don't need to have per-query lifetime.)
250+
* We rely on the caller to evaluate plan correlation values, if
251+
* necessary. However we still need to record the fact that the values
252+
* (might have) changed, otherwise the ExecReScan() below won't know that
253+
* nodes need to be rescanned.
254254
*/
255-
Assert(list_length(subplan->parParam) == list_length(node->args));
256-
257-
forboth(l, subplan->parParam, pvar, node->args)
255+
foreach(l, subplan->parParam)
258256
{
259257
int paramid = lfirst_int(l);
260-
ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]);
261258

262-
prm->value = ExecEvalExprSwitchContext((ExprState *) lfirst(pvar),
263-
econtext,
264-
&(prm->isnull));
265259
planstate->chgParam = bms_add_member(planstate->chgParam, paramid);
266260
}
267261

268-
/*
269-
* Now that we've set up its parameters, we can reset the subplan.
270-
*/
262+
/* with that done, we can reset the subplan */
271263
ExecReScan(planstate);
272264

273265
/*
@@ -817,6 +809,10 @@ slotNoNulls(TupleTableSlot *slot)
817809
* as well as regular SubPlans. Note that we don't link the SubPlan into
818810
* the parent's subPlan list, because that shouldn't happen for InitPlans.
819811
* Instead, ExecInitExpr() does that one part.
812+
*
813+
* We also rely on ExecInitExpr(), more precisely ExecInitSubPlanExpr(), to
814+
* evaluate input parameters, as that allows them to be evaluated as part of
815+
* the expression referencing the SubPlan.
820816
* ----------------------------------------------------------------
821817
*/
822818
SubPlanState *
@@ -844,7 +840,6 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
844840

845841
/* Initialize subexpressions */
846842
sstate->testexpr = ExecInitExpr((Expr *) subplan->testexpr, parent);
847-
sstate->args = ExecInitExprList(subplan->args, parent);
848843

849844
/*
850845
* initialize my state
@@ -1107,7 +1102,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
11071102
elog(ERROR, "ANY/ALL subselect unsupported as initplan");
11081103
if (subLinkType == CTE_SUBLINK)
11091104
elog(ERROR, "CTE subplans should not be executed via ExecSetParamPlan");
1110-
if (subplan->parParam || node->args)
1105+
if (subplan->parParam || subplan->args)
11111106
elog(ERROR, "correlated subplans should not be executed via ExecSetParamPlan");
11121107

11131108
/*

src/backend/jit/llvm/llvmjit_expr.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,12 @@ llvm_compile_expr(ExprState *state)
11451145
break;
11461146
}
11471147

1148+
case EEOP_PARAM_SET:
1149+
build_EvalXFunc(b, mod, "ExecEvalParamSet",
1150+
v_state, op, v_econtext);
1151+
LLVMBuildBr(b, opblocks[opno + 1]);
1152+
break;
1153+
11481154
case EEOP_SBSREF_SUBSCRIPTS:
11491155
{
11501156
int jumpdone = op->d.sbsref_subscript.jumpdone;

src/backend/jit/llvm/llvmjit_types.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ void *referenced_functions[] =
160160
ExecEvalNextValueExpr,
161161
ExecEvalParamExec,
162162
ExecEvalParamExtern,
163+
ExecEvalParamSet,
163164
ExecEvalRow,
164165
ExecEvalRowNotNull,
165166
ExecEvalRowNull,

src/include/executor/execExpr.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,8 @@ typedef enum ExprEvalOp
160160
EEOP_PARAM_EXEC,
161161
EEOP_PARAM_EXTERN,
162162
EEOP_PARAM_CALLBACK,
163+
/* set PARAM_EXEC value */
164+
EEOP_PARAM_SET,
163165

164166
/* return CaseTestExpr value */
165167
EEOP_CASE_TESTVAL,
@@ -384,7 +386,7 @@ typedef struct ExprEvalStep
384386
ExprEvalRowtypeCache rowcache;
385387
} nulltest_row;
386388

387-
/* for EEOP_PARAM_EXEC/EXTERN */
389+
/* for EEOP_PARAM_EXEC/EXTERN and EEOP_PARAM_SET */
388390
struct
389391
{
390392
int paramid; /* numeric ID for parameter */
@@ -800,6 +802,8 @@ extern void ExecEvalFuncExprStrictFusage(ExprState *state, ExprEvalStep *op,
800802
ExprContext *econtext);
801803
extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op,
802804
ExprContext *econtext);
805+
extern void ExecEvalParamSet(ExprState *state, ExprEvalStep *op,
806+
ExprContext *econtext);
803807
extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op,
804808
ExprContext *econtext);
805809
extern void ExecEvalCoerceViaIOSafe(ExprState *state, ExprEvalStep *op);

src/include/nodes/execnodes.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -961,7 +961,6 @@ typedef struct SubPlanState
961961
struct PlanState *planstate; /* subselect plan's state tree */
962962
struct PlanState *parent; /* parent plan node's state tree */
963963
ExprState *testexpr; /* state of combining expression */
964-
List *args; /* states of argument expression(s) */
965964
HeapTuple curTuple; /* copy of most recent tuple from subplan */
966965
Datum curArray; /* most recent array from ARRAY() subplan */
967966
/* these are used when hashing the subselect's output: */

0 commit comments

Comments
 (0)