8000 Evaluate arguments of correlated SubPlans in the referencing ExprState · postgres/postgres@a7f107d · GitHub
[go: up one dir, main page]

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
  • src
    • backend
      • executor
        • < 8000 div class="PRIVATE_VisuallyHidden prc-TreeView-TreeViewVisuallyHidden-4-mPv" aria-hidden="true" id=":RjaqmtddabH1:">
          execExprInterp.c
  • jit/llvm
  • include
  • 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)
    0