diff options
Diffstat (limited to 'src/backend/executor/nodeSubplan.c')
-rw-r--r-- | src/backend/executor/nodeSubplan.c | 80 |
1 files changed, 72 insertions, 8 deletions
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index 5d02d9420b1..401bad45b59 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -1,7 +1,15 @@ /*------------------------------------------------------------------------- * * nodeSubplan.c - * routines to support subselects + * routines to support sub-selects appearing in expressions + * + * This module is concerned with executing SubPlan expression nodes, which + * should not be confused with sub-SELECTs appearing in FROM. SubPlans are + * divided into "initplans", which are those that need only one evaluation per + * query (among other restrictions, this requires that they don't use any + * direct correlation variables from the parent plan level), and "regular" + * subplans, which are re-evaluated every time their result is required. + * * * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California @@ -54,6 +62,8 @@ static bool slotNoNulls(TupleTableSlot *slot); /* ---------------------------------------------------------------- * ExecSubPlan + * + * This is the main entry point for execution of a regular SubPlan. * ---------------------------------------------------------------- */ static Datum @@ -72,7 +82,7 @@ ExecSubPlan(SubPlanState *node, /* Sanity checks */ if (subplan->subLinkType == CTE_SUBLINK) elog(ERROR, "CTE subplans should not be executed via ExecSubPlan"); - if (subplan->setParam != NIL) + if (subplan->setParam != NIL && subplan->subLinkType != MULTIEXPR_SUBLINK) elog(ERROR, "cannot set parent params from subquery"); /* Select appropriate evaluation strategy */ @@ -224,6 +234,32 @@ ExecScanSubPlan(SubPlanState *node, ArrayBuildState *astate = NULL; /* + * MULTIEXPR subplans, when "executed", just return NULL; but first we + * mark the subplan's output parameters as needing recalculation. (This + * is a bit of a hack: it relies on the subplan appearing later in its + * targetlist than any of the referencing Params, so that all the Params + * have been evaluated before we re-mark them for the next evaluation + * cycle. But in general resjunk tlist items appear after non-resjunk + * ones, so this should be safe.) Unlike ExecReScanSetParamPlan, we do + * *not* set bits in the parent plan node's chgParam, because we don't + * want to cause a rescan of the parent. + */ + if (subLinkType == MULTIEXPR_SUBLINK) + { + EState *estate = node->parent->state; + + foreach(l, subplan->setParam) + { + int paramid = lfirst_int(l); + ParamExecData *prm = &(estate->es_param_exec_vals[paramid]); + + prm->execPlan = node; + } + *isNull = true; + return (Datum) 0; + } + + /* * We are probably in a short-lived expression-evaluation context. Switch * to the per-query context for manipulating the child plan's chgParam, * calling ExecProcNode on it, etc. @@ -667,6 +703,9 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) sstate->planstate = (PlanState *) list_nth(estate->es_subplanstates, subplan->plan_id - 1); + /* ... and to its parent's state */ + sstate->parent = parent; + /* Initialize subexpressions */ sstate->testexpr = ExecInitExpr((Expr *) subplan->testexpr, parent); sstate->args = (List *) ExecInitExpr((Expr *) subplan->args, parent); @@ -690,15 +729,16 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) sstate->cur_eq_funcs = NULL; /* - * If this plan is un-correlated or undirect correlated one and want to - * set params for parent plan then mark parameters as needing evaluation. + * If this is an initplan or MULTIEXPR subplan, it has output parameters + * that the parent plan will use, so mark those parameters as needing + * evaluation. We don't actually run the subplan until we first need one + * of its outputs. * * A CTE subplan's output parameter is never to be evaluated in the normal * way, so skip this in that case. * - * Note that in the case of un-correlated subqueries we don't care about - * setting parent->chgParam here: indices take care about it, for others - - * it doesn't matter... + * Note that we don't set parent->chgParam here: the parent plan hasn't + * been run yet, so no need to force it to re-run. */ if (subplan->setParam != NIL && subplan->subLinkType != CTE_SUBLINK) { @@ -890,7 +930,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) /* ---------------------------------------------------------------- * ExecSetParamPlan * - * Executes an InitPlan subplan and sets its output parameters. + * Executes a subplan and sets its output parameters. * * This is called from ExecEvalParamExec() when the value of a PARAM_EXEC * parameter is requested and the param's execPlan field is set (indicating @@ -908,6 +948,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) SubLinkType subLinkType = subplan->subLinkType; MemoryContext oldcontext; TupleTableSlot *slot; + ListCell *pvar; ListCell *l; bool found = false; ArrayBuildState *astate = NULL; @@ -924,6 +965,27 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); /* + * Set Params of this plan from parent plan correlation values. (Any + * calculation we have to do is done in the parent econtext, since the + * Param values don't need to have per-query lifetime.) Currently, we + * expect only MULTIEXPR_SUBLINK plans to have any correlation values. + */ + Assert(subplan->parParam == NIL || subLinkType == MULTIEXPR_SUBLINK); + Assert(list_length(subplan->parParam) == list_length(node->args)); + + forboth(l, subplan->parParam, pvar, node->args) + { + int paramid = lfirst_int(l); + ParamExecData *prm = &(econtext->ecxt_param_exec_vals[paramid]); + + prm->value = ExecEvalExprSwitchContext((ExprState *) lfirst(pvar), + econtext, + &(prm->isnull), + NULL); + planstate->chgParam = bms_add_member(planstate->chgParam, paramid); + } + + /* * Run the plan. (If it needs to be rescanned, the first ExecProcNode * call will take care of that.) */ @@ -964,6 +1026,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) if (found && (subLinkType == EXPR_SUBLINK || + subLinkType == MULTIEXPR_SUBLINK || subLinkType == ROWCOMPARE_SUBLINK)) ereport(ERROR, (errcode(ERRCODE_CARDINALITY_VIOLATION), @@ -1035,6 +1098,7 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) } else { + /* For other sublink types, set all the output params to NULL */ foreach(l, subplan->setParam) { int paramid = lfirst_int(l); |