diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2014-06-18 13:22:25 -0400 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2014-06-18 13:22:34 -0400 |
commit | 8f889b1083f38f4f5b3bd3512008a3f60e939244 (patch) | |
tree | 68c2e242c88245ea0d3b9329e1e27c78a8e70eaf /src/backend/executor/nodeSubplan.c | |
parent | 230ba02d855de7fac31bfb6af25ebd4ae052640b (diff) | |
download | postgresql-8f889b1083f38f4f5b3bd3512008a3f60e939244.tar.gz postgresql-8f889b1083f38f4f5b3bd3512008a3f60e939244.zip |
Implement UPDATE tab SET (col1,col2,...) = (SELECT ...), ...
This SQL-standard feature allows a sub-SELECT yielding multiple columns
(but only one row) to be used to compute the new values of several columns
to be updated. While the same results can be had with an independent
sub-SELECT per column, such a workaround can require a great deal of
duplicated computation.
The standard actually says that the source for a multi-column assignment
could be any row-valued expression. The implementation used here is
tightly tied to our existing sub-SELECT support and can't handle other
cases; the Bison grammar would have some issues with them too. However,
I don't feel too bad about this since other cases can be converted into
sub-SELECTs. For instance, "SET (a,b,c) = row_valued_function(x)" could
be written "SET (a,b,c) = (SELECT * FROM row_valued_function(x))".
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); |