aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/execExpr.c174
-rw-r--r--src/backend/executor/nodeSubplan.c119
2 files changed, 179 insertions, 114 deletions
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 502445acecb..d89445ee7ba 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -51,12 +51,15 @@
#include "utils/typcache.h"
-typedef struct LastAttnumInfo
+typedef struct ExprSetupInfo
{
+ /* Highest attribute numbers fetched from inner/outer/scan tuple slots: */
AttrNumber last_inner;
AttrNumber last_outer;
AttrNumber last_scan;
-} LastAttnumInfo;
+ /* MULTIEXPR SubPlan nodes appearing in the expression: */
+ List *multiexpr_subplans;
+} ExprSetupInfo;
static void ExecReadyExpr(ExprState *state);
static void ExecInitExprRec(Expr *node, ExprState *state,
@@ -64,9 +67,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 ExecInitExprSlots(ExprState *state, Node *node);
-static void ExecPushExprSlots(ExprState *state, LastAttnumInfo *info);
-static bool get_last_attnums_walker(Node *node, LastAttnumInfo *info);
+static void ExecCreateExprSetupSteps(ExprState *state, Node *node);
+static void ExecPushExprSetupSteps(ExprState *state, ExprSetupInfo *info);
+static bool expr_setup_walker(Node *node, ExprSetupInfo *info);
static bool ExecComputeSlotInfo(ExprState *state, ExprEvalStep *op);
static void ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable,
ExprState *state);
@@ -135,8 +138,8 @@ ExecInitExpr(Expr *node, PlanState *parent)
state->parent = parent;
state->ext_params = NULL;
- /* Insert EEOP_*_FETCHSOME steps as needed */
- ExecInitExprSlots(state, (Node *) node);
+ /* Insert setup steps as needed */
+ ExecCreateExprSetupSteps(state, (Node *) node);
/* Compile the expression proper */
ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
@@ -172,8 +175,8 @@ ExecInitExprWithParams(Expr *node, ParamListInfo ext_params)
state->parent = NULL;
state->ext_params = ext_params;
- /* Insert EEOP_*_FETCHSOME steps as needed */
- ExecInitExprSlots(state, (Node *) node);
+ /* Insert setup steps as needed */
+ ExecCreateExprSetupSteps(state, (Node *) node);
/* Compile the expression proper */
ExecInitExprRec(node, state, &state->resvalue, &state->resnull);
@@ -227,8 +230,8 @@ ExecInitQual(List *qual, PlanState *parent)
/* mark expression as to be used with ExecQual() */
state->flags = EEO_FLAG_IS_QUAL;
- /* Insert EEOP_*_FETCHSOME steps as needed */
- ExecInitExprSlots(state, (Node *) qual);
+ /* Insert setup steps as needed */
+ ExecCreateExprSetupSteps(state, (Node *) qual);
/*
* ExecQual() needs to return false for an expression returning NULL. That
@@ -371,8 +374,8 @@ ExecBuildProjectionInfo(List *targetList,
state->resultslot = slot;
- /* Insert EEOP_*_FETCHSOME steps as needed */
- ExecInitExprSlots(state, (Node *) targetList);
+ /* Insert setup steps as needed */
+ ExecCreateExprSetupSteps(state, (Node *) targetList);
/* Now compile each tlist column */
foreach(lc, targetList)
@@ -523,7 +526,7 @@ ExecBuildUpdateProjection(List *targetList,
int nAssignableCols;
bool sawJunk;
Bitmapset *assignedCols;
- LastAttnumInfo deform = {0, 0, 0};
+ ExprSetupInfo deform = {0, 0, 0, NIL};
ExprEvalStep scratch = {0};
int outerattnum;
ListCell *lc,
@@ -602,17 +605,18 @@ ExecBuildUpdateProjection(List *targetList,
* number of columns of the "outer" tuple.
*/
if (evalTargetList)
- get_last_attnums_walker((Node *) targetList, &deform);
+ expr_setup_walker((Node *) targetList, &deform);
else
deform.last_outer = nAssignableCols;
- ExecPushExprSlots(state, &deform);
+ ExecPushExprSetupSteps(state, &deform);
/*
* Now generate code to evaluate the tlist's assignable expressions or
* fetch them from the outer tuple, incidentally validating that they'll
* be of the right data type. The checks above ensure that the forboth()
- * will iterate over exactly the non-junk columns.
+ * will iterate over exactly the non-junk columns. Note that we don't
+ * bother evaluating any remaining resjunk columns.
*/
outerattnum = 0;
forboth(lc, targetList, lc2, targetColnos)
@@ -676,22 +680,6 @@ ExecBuildUpdateProjection(List *targetList,
}
/*
- * If we're evaluating the tlist, must evaluate any resjunk columns too.
- * (This matters for things like MULTIEXPR_SUBLINK SubPlans.)
- */
- if (evalTargetList)
- {
- for_each_cell(lc, targetList, lc)
- {
- TargetEntry *tle = lfirst_node(TargetEntry, lc);
-
- Assert(tle->resjunk);
- ExecInitExprRec(tle->expr, state,
- &state->resvalue, &state->resnull);
- }
- }
-
- /*
* Now generate code to copy over any old columns that were not assigned
* to, and to ensure that dropped columns are set to NULL.
*/
@@ -1401,6 +1389,21 @@ ExecInitExprRec(Expr *node, ExprState *state,
SubPlan *subplan = (SubPlan *) node;
SubPlanState *sstate;
+ /*
+ * Real execution of a MULTIEXPR SubPlan has already been
+ * done. What we have to do here is return a dummy NULL record
+ * value in case this targetlist element is assigned
+ * someplace.
+ */
+ if (subplan->subLinkType == MULTIEXPR_SUBLINK)
+ {
+ scratch.opcode = EEOP_CONST;
+ scratch.d.constval.value = (Datum) 0;
+ scratch.d.constval.isnull = true;
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
if (!state->parent)
elog(ERROR, "SubPlan found with no parent plan");
@@ -2552,36 +2555,38 @@ ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
}
/*
- * Add expression steps deforming the ExprState's inner/outer/scan slots
- * as much as required by the expression.
+ * Add expression steps performing setup that's needed before any of the
+ * main execution of the expression.
*/
static void
-ExecInitExprSlots(ExprState *state, Node *node)
+ExecCreateExprSetupSteps(ExprState *state, Node *node)
{
- LastAttnumInfo info = {0, 0, 0};
+ ExprSetupInfo info = {0, 0, 0, NIL};
- /*
- * Figure out which attributes we're going to need.
- */
- get_last_attnums_walker(node, &info);
+ /* Prescan to find out what we need. */
+ expr_setup_walker(node, &info);
- ExecPushExprSlots(state, &info);
+ /* And generate those steps. */
+ ExecPushExprSetupSteps(state, &info);
}
/*
- * Add steps deforming the ExprState's inner/out/scan slots as much as
- * indicated by info. This is useful when building an ExprState covering more
- * than one expression.
+ * Add steps performing expression setup as indicated by "info".
+ * This is useful when building an ExprState covering more than one expression.
*/
static void
-ExecPushExprSlots(ExprState *state, LastAttnumInfo *info)
+ExecPushExprSetupSteps(ExprState *state, ExprSetupInfo *info)
{
ExprEvalStep scratch = {0};
+ ListCell *lc;
scratch.resvalue = NULL;
scratch.resnull = NULL;
- /* Emit steps as needed */
+ /*
+ * Add steps deforming the ExprState's inner/outer/scan slots as much as
+ * required by any Vars appearing in the expression.
+ */
if (info->last_inner > 0)
{
scratch.opcode = EEOP_INNER_FETCHSOME;
@@ -2612,13 +2617,48 @@ ExecPushExprSlots(ExprState *state, LastAttnumInfo *info)
if (ExecComputeSlotInfo(state, &scratch))
ExprEvalPushStep(state, &scratch);
}
+
+ /*
+ * Add steps to execute any MULTIEXPR SubPlans appearing in the
+ * expression. We need to evaluate these before any of the Params
+ * referencing their outputs are used, but after we've prepared for any
+ * Var references they may contain. (There cannot be cross-references
+ * between MULTIEXPR SubPlans, so we needn't worry about their order.)
+ */
+ 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);
+ }
}
/*
- * get_last_attnums_walker: expression walker for ExecInitExprSlots
+ * expr_setup_walker: expression walker for ExecCreateExprSetupSteps
*/
static bool
-get_last_attnums_walker(Node *node, LastAttnumInfo *info)
+expr_setup_walker(Node *node, ExprSetupInfo *info)
{
if (node == NULL)
return false;
@@ -2646,6 +2686,16 @@ get_last_attnums_walker(Node *node, LastAttnumInfo *info)
return false;
}
+ /* Collect all MULTIEXPR SubPlans, too */
+ if (IsA(node, SubPlan))
+ {
+ SubPlan *subplan = (SubPlan *) node;
+
+ if (subplan->subLinkType == MULTIEXPR_SUBLINK)
+ info->multiexpr_subplans = lappend(info->multiexpr_subplans,
+ subplan);
+ }
+
/*
* Don't examine the arguments or filters of Aggrefs or WindowFuncs,
* because those do not represent expressions to be evaluated within the
@@ -2658,7 +2708,7 @@ get_last_attnums_walker(Node *node, LastAttnumInfo *info)
return false;
if (IsA(node, GroupingFunc))
return false;
- return expression_tree_walker(node, get_last_attnums_walker,
+ return expression_tree_walker(node, expr_setup_walker,
(void *) info);
}
@@ -3277,7 +3327,7 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
PlanState *parent = &aggstate->ss.ps;
ExprEvalStep scratch = {0};
bool isCombine = DO_AGGSPLIT_COMBINE(aggstate->aggsplit);
- LastAttnumInfo deform = {0, 0, 0};
+ ExprSetupInfo deform = {0, 0, 0, NIL};
state->expr = (Expr *) aggstate;
state->parent = parent;
@@ -3293,18 +3343,18 @@ ExecBuildAggTrans(AggState *aggstate, AggStatePerPhase phase,
{
AggStatePerTrans pertrans = &aggstate->pertrans[transno];
- get_last_attnums_walker((Node *) pertrans->aggref->aggdirectargs,
- &deform);
- get_last_attnums_walker((Node *) pertrans->aggref->args,
- &deform);
- get_last_attnums_walker((Node *) pertrans->aggref->aggorder,
- &deform);
- get_last_attnums_walker((Node *) pertrans->aggref->aggdistinct,
- &deform);
- get_last_attnums_walker((Node *) pertrans->aggref->aggfilter,
- &deform);
+ expr_setup_walker((Node *) pertrans->aggref->aggdirectargs,
+ &deform);
+ expr_setup_walker((Node *) pertrans->aggref->args,
+ &deform);
+ expr_setup_walker((Node *) pertrans->aggref->aggorder,
+ &deform);
+ expr_setup_walker((Node *) pertrans->aggref->aggdistinct,
+ &deform);
+ expr_setup_walker((Node *) pertrans->aggref->aggfilter,
+ &deform);
}
- ExecPushExprSlots(state, &deform);
+ ExecPushExprSetupSteps(state, &deform);
/*
* Emit instructions for each transition value / grouping set combination.
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
index 43b36dcd3b4..a33efce3fe1 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -235,32 +235,6 @@ ExecScanSubPlan(SubPlanState *node,
ListCell *l;
ArrayBuildStateAny *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;
- }
-
/* Initialize ArrayBuildStateAny in caller's context, if needed */
if (subLinkType == ARRAY_SUBLINK)
astate = initArrayResultAny(subplan->firstColType,
@@ -312,6 +286,11 @@ ExecScanSubPlan(SubPlanState *node,
* NULL. Assuming we get a tuple, we just use its first column (there can
* be only one non-junk column in this case).
*
+ * For MULTIEXPR_SUBLINK, we push the per-column subplan outputs out to
+ * the setParams and then return a dummy false value. There must not be
+ * multiple tuples returned from the subplan; if zero tuples are produced,
+ * set the setParams to NULL.
+ *
* For ARRAY_SUBLINK we allow the subplan to produce any number of tuples,
* and form an array of the first column's values. Note in particular
* that we produce a zero-element array if no tuples are produced (this is
@@ -363,6 +342,47 @@ ExecScanSubPlan(SubPlanState *node,
continue;
}
+ if (subLinkType == MULTIEXPR_SUBLINK)
+ {
+ /* cannot allow multiple input tuples for MULTIEXPR sublink */
+ if (found)
+ ereport(ERROR,
+ (errcode(ERRCODE_CARDINALITY_VIOLATION),
+ errmsg("more than one row returned by a subquery used as an expression")));
+ found = true;
+
+ /*
+ * We need to copy the subplan's tuple in case any result is of
+ * pass-by-ref type --- our output values will point into this
+ * copied tuple! Can't use the subplan's instance of the tuple
+ * since it won't still be valid after next ExecProcNode() call.
+ * node->curTuple keeps track of the copied tuple for eventual
+ * freeing.
+ */
+ if (node->curTuple)
+ heap_freetuple(node->curTuple);
+ node->curTuple = ExecCopySlotHeapTuple(slot);
+
+ /*
+ * Now set all the setParam params from the columns of the tuple
+ */
+ col = 1;
+ foreach(plst, subplan->setParam)
+ {
+ int paramid = lfirst_int(plst);
+ ParamExecData *prmdata;
+
+ prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
+ Assert(prmdata->execPlan == NULL);
+ prmdata->value = heap_getattr(node->curTuple, col, tdesc,
+ &(prmdata->isnull));
+ col++;
+ }
+
+ /* keep scanning subplan to make sure there's only one tuple */
+ continue;
+ }
+
if (subLinkType == ARRAY_SUBLINK)
{
Datum dvalue;
@@ -458,6 +478,20 @@ ExecScanSubPlan(SubPlanState *node,
result = (Datum) 0;
*isNull = true;
}
+ else if (subLinkType == MULTIEXPR_SUBLINK)
+ {
+ /* We don't care about function result, but set the setParams */
+ foreach(l, subplan->setParam)
+ {
+ int paramid = lfirst_int(l);
+ ParamExecData *prmdata;
+
+ prmdata = &(econtext->ecxt_param_exec_vals[paramid]);
+ Assert(prmdata->execPlan == NULL);
+ prmdata->value = (Datum) 0;
+ prmdata->isnull = true;
+ }
+ }
}
return result;
@@ -833,10 +867,9 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
sstate->cur_eq_funcs = NULL;
/*
- * 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.
+ * If this is an initplan, 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.
@@ -844,7 +877,8 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
* 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)
+ if (subplan->setParam != NIL && subplan->parParam == NIL &&
+ subplan->subLinkType != CTE_SUBLINK)
{
ListCell *lst;
@@ -1064,7 +1098,6 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
ScanDirection dir = estate->es_direction;
MemoryContext oldcontext;
TupleTableSlot *slot;
- ListCell *pvar;
ListCell *l;
bool found = false;
ArrayBuildStateAny *astate = NULL;
@@ -1074,6 +1107,8 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
elog(ERROR, "ANY/ALL subselect unsupported as initplan");
if (subLinkType == CTE_SUBLINK)
elog(ERROR, "CTE subplans should not be executed via ExecSetParamPlan");
+ if (subplan->parParam || node->args)
+ elog(ERROR, "correlated subplans should not be executed via ExecSetParamPlan");
/*
* Enforce forward scan direction regardless of caller. It's hard but not
@@ -1092,26 +1127,6 @@ 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));
- 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.)
*/