aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeSubplan.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/nodeSubplan.c')
-rw-r--r--src/backend/executor/nodeSubplan.c80
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);