1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-22 12:22:45 +03:00

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
This commit is contained in:
Andres Freund
2024-07-31 18:11:49 -07:00
parent e6a9637488
commit a7f107df2b
8 changed files with 125 additions and 52 deletions

View File

@@ -69,6 +69,9 @@ static void ExecInitExprRec(Expr *node, ExprState *state,
static void ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args,
Oid funcid, Oid inputcollid,
ExprState *state);
static void ExecInitSubPlanExpr(SubPlan *subplan,
ExprState *state,
Datum *resv, bool *resnull);
static void ExecCreateExprSetupSteps(ExprState *state, Node *node);
static void ExecPushExprSetupSteps(ExprState *state, ExprSetupInfo *info);
static bool expr_setup_walker(Node *node, ExprSetupInfo *info);
@@ -1406,7 +1409,6 @@ ExecInitExprRec(Expr *node, ExprState *state,
case T_SubPlan:
{
SubPlan *subplan = (SubPlan *) node;
SubPlanState *sstate;
/*
* Real execution of a MULTIEXPR SubPlan has already been
@@ -1423,19 +1425,7 @@ ExecInitExprRec(Expr *node, ExprState *state,
break;
}
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;
ExprEvalPushStep(state, &scratch);
ExecInitSubPlanExpr(subplan, state, resv, resnull);
break;
}
@@ -2715,6 +2705,70 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
}
}
/*
* Append the steps necessary for the evaluation of a SubPlan node to
* ExprState->steps.
*
* subplan - SubPlan 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
ExecInitSubPlanExpr(SubPlan *subplan,
ExprState *state,
Datum *resv, bool *resnull)
{
ExprEvalStep scratch = {0};
SubPlanState *sstate;
ListCell *pvar;
ListCell *l;
if (!state->parent)
elog(ERROR, "SubPlan found with no parent plan");
/*
* Generate steps to evaluate input arguments for the subplan.
*
* We evaluate the argument expressions into ExprState's resvalue/resnull,
* and then use PARAM_SET to update the parameter. We do that, instead of
* evaluating directly into the param, to avoid depending on the pointer
* value remaining stable / being included in the generated expression. No
* danger of conflicts with other uses of resvalue/resnull as storing and
* using the value always is in subsequent steps.
*
* Any calculation we have to do can be done in the parent econtext, since
* the Param values don't need to have per-query lifetime.
*/
Assert(list_length(subplan->parParam) == list_length(subplan->args));
forboth(l, subplan->parParam, pvar, subplan->args)
{
int paramid = lfirst_int(l);
Expr *arg = (Expr *) lfirst(pvar);
ExecInitExprRec(arg, state,
&state->resvalue, &state->resnull);
scratch.opcode = EEOP_PARAM_SET;
scratch.d.param.paramid = paramid;
/* paramtype's not actually used, but we might as well fill it */
scratch.d.param.paramtype = exprType((Node *) arg);
ExprEvalPushStep(state, &scratch);
}
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.resvalue = resv;
scratch.resnull = resnull;
scratch.d.subplan.sstate = sstate;
ExprEvalPushStep(state, &scratch);
}
/*
* Add expression steps performing setup that's needed before any of the
* main execution of the expression.
@@ -2789,29 +2843,12 @@ ExecPushExprSetupSteps(ExprState *state, ExprSetupInfo *info)
foreach(lc, info->multiexpr_subplans)
{
SubPlan *subplan = (SubPlan *) lfirst(lc);
SubPlanState *sstate;
Assert(subplan->subLinkType == MULTIEXPR_SUBLINK);
/* This should match what ExecInitExprRec does for other SubPlans: */
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;
/* The result can be ignored, but we better put it somewhere */
scratch.resvalue = &state->resvalue;
scratch.resnull = &state->resnull;
ExprEvalPushStep(state, &scratch);
ExecInitSubPlanExpr(subplan, state,
&state->resvalue, &state->resnull);
}
}