aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/executor/execExpr.c149
-rw-r--r--src/backend/executor/nodeSubplan.c134
-rw-r--r--src/backend/optimizer/plan/planner.c4
-rw-r--r--src/backend/optimizer/plan/subselect.c129
-rw-r--r--src/include/nodes/primnodes.h10
-rw-r--r--src/include/optimizer/subselect.h1
-rw-r--r--src/test/regress/expected/inherit.out158
-rw-r--r--src/test/regress/sql/inherit.sql48
8 files changed, 306 insertions, 327 deletions
diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c
index 33f5e3a8fe7..c32dbd6e63f 100644
--- a/src/backend/executor/execExpr.c
+++ b/src/backend/executor/execExpr.c
@@ -50,12 +50,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,
@@ -63,9 +66,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
@@ -397,8 +400,8 @@ ExecBuildProjectionInfoExt(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)
@@ -1119,6 +1122,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");
@@ -2287,36 +2305,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;
@@ -2347,13 +2367,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;
@@ -2381,6 +2436,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
@@ -2393,7 +2458,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);
}
@@ -2985,7 +3050,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;
@@ -3001,18 +3066,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 c542464e5a8..88604884cd8 100644
--- a/src/backend/executor/nodeSubplan.c
+++ b/src/backend/executor/nodeSubplan.c
@@ -235,47 +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.
- *
- * Note: we are also relying on MULTIEXPR SubPlans not sharing any output
- * parameters with other SubPlans, because if one does then it is unclear
- * which SubPlanState node the parameter's execPlan field will be pointing
- * to when we come to evaluate the parameter. We can allow plain initplan
- * SubPlans to share output parameters, because it doesn't actually matter
- * which initplan SubPlan we reference as long as they all point to the
- * same underlying subplan. However, that fails to hold for MULTIEXPRs
- * because they can have non-empty args lists, and the "same" args might
- * have mutated into different forms in different parts of a plan tree.
- * There is not a problem in ordinary queries because MULTIEXPR will
- * appear only in an UPDATE's top-level target list, so it won't get
- * duplicated anyplace. However, when inheritance_planner clones a
- * partially-planned targetlist it must take care to assign non-duplicate
- * param IDs to the cloned copy.
- */
- 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,
@@ -327,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
@@ -378,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;
@@ -473,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;
@@ -848,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.
@@ -859,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;
@@ -1079,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;
@@ -1089,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
@@ -1107,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.)
*/
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 27c665ac126..60e7fda6a9b 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1606,10 +1606,6 @@ inheritance_planner(PlannerInfo *root)
/* and we haven't created PlaceHolderInfos, either */
Assert(subroot->placeholder_list == NIL);
- /* Fix MULTIEXPR_SUBLINK params if any */
- if (root->multiexpr_params)
- SS_make_multiexprs_unique(root, subroot);
-
/* Generate Path(s) for accessing this result relation */
grouping_planner(subroot, true, 0.0 /* retrieve all tuples */ );
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 3e65af2dced..f356b5cfb0a 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -86,7 +86,6 @@ static bool subplan_is_hashable(Plan *plan);
static bool testexpr_is_hashable(Node *testexpr, List *param_ids);
static bool test_opexpr_is_hashable(OpExpr *testexpr, List *param_ids);
static bool hash_ok_operator(OpExpr *expr);
-static bool SS_make_multiexprs_unique_walker(Node *node, void *context);
static bool contain_dml(Node *node);
static bool contain_dml_walker(Node *node, void *context);
static bool contain_outer_selfref(Node *node);
@@ -853,134 +852,6 @@ hash_ok_operator(OpExpr *expr)
}
}
-/*
- * SS_make_multiexprs_unique
- *
- * After cloning an UPDATE targetlist that contains MULTIEXPR_SUBLINK
- * SubPlans, inheritance_planner() must call this to assign new, unique Param
- * IDs to the cloned MULTIEXPR_SUBLINKs' output parameters. See notes in
- * ExecScanSubPlan.
- *
- * We do not need to renumber Param IDs for MULTIEXPR_SUBLINK plans that are
- * initplans, because those don't have input parameters that could cause
- * confusion. Such initplans will not appear in the targetlist anyway, but
- * they still complicate matters because the surviving regular subplans might
- * not have consecutive subLinkIds.
- */
-void
-SS_make_multiexprs_unique(PlannerInfo *root, PlannerInfo *subroot)
-{
- List *param_mapping = NIL;
- ListCell *lc;
-
- /*
- * Find MULTIEXPR SubPlans in the cloned query. We need only look at the
- * top level of the targetlist.
- */
- foreach(lc, subroot->parse->targetList)
- {
- TargetEntry *tent = (TargetEntry *) lfirst(lc);
- SubPlan *splan;
- Plan *plan;
- List *params;
- int oldId,
- newId;
- ListCell *lc2;
-
- if (!IsA(tent->expr, SubPlan))
- continue;
- splan = (SubPlan *) tent->expr;
- if (splan->subLinkType != MULTIEXPR_SUBLINK)
- continue;
-
- /* Found one, get the associated subplan */
- plan = (Plan *) list_nth(root->glob->subplans, splan->plan_id - 1);
-
- /*
- * Generate new PARAM_EXEC Param nodes, and overwrite splan->setParam
- * with their IDs. This is just like what build_subplan did when it
- * made the SubPlan node we're cloning. But because the param IDs are
- * assigned globally, we'll get new IDs. (We assume here that the
- * subroot's tlist is a clone we can scribble on.)
- */
- params = generate_subquery_params(root,
- plan->targetlist,
- &splan->setParam);
-
- /*
- * Append the new replacement-Params list to root->multiexpr_params.
- * Then its index in that list becomes the new subLinkId of the
- * SubPlan.
- */
- root->multiexpr_params = lappend(root->multiexpr_params, params);
- oldId = splan->subLinkId;
- newId = list_length(root->multiexpr_params);
- Assert(newId > oldId);
- splan->subLinkId = newId;
-
- /*
- * Add a mapping entry to param_mapping so that we can update the
- * associated Params below. Leave zeroes in the list for any
- * subLinkIds we don't encounter; those must have been converted to
- * initplans.
- */
- while (list_length(param_mapping) < oldId)
- param_mapping = lappend_int(param_mapping, 0);
- lc2 = list_nth_cell(param_mapping, oldId - 1);
- lfirst_int(lc2) = newId;
- }
-
- /*
- * Unless all the MULTIEXPRs were converted to initplans, we must now find
- * the Param nodes that reference the MULTIEXPR outputs and update their
- * sublink IDs so they'll reference the new outputs. While such Params
- * must be in the cloned targetlist, they could be buried under stuff such
- * as FieldStores and SubscriptingRefs and type coercions.
- */
- if (param_mapping != NIL)
- SS_make_multiexprs_unique_walker((Node *) subroot->parse->targetList,
- (void *) param_mapping);
-}
-
-/*
- * Locate PARAM_MULTIEXPR Params in an expression tree, and update as needed.
- * (We can update-in-place because the tree was already copied.)
- */
-static bool
-SS_make_multiexprs_unique_walker(Node *node, void *context)
-{
- if (node == NULL)
- return false;
- if (IsA(node, Param))
- {
- Param *p = (Param *) node;
- List *param_mapping = (List *) context;
- int subqueryid;
- int colno;
- int newId;
-
- if (p->paramkind != PARAM_MULTIEXPR)
- return false;
- subqueryid = p->paramid >> 16;
- colno = p->paramid & 0xFFFF;
-
- /*
- * If subqueryid doesn't have a mapping entry, it must refer to an
- * initplan, so don't change the Param.
- */
- Assert(subqueryid > 0);
- if (subqueryid > list_length(param_mapping))
- return false;
- newId = list_nth_int(param_mapping, subqueryid - 1);
- if (newId == 0)
- return false;
- p->paramid = (newId << 16) + colno;
- return false;
- }
- return expression_tree_walker(node, SS_make_multiexprs_unique_walker,
- context);
-}
-
/*
* SS_process_ctes: process a query's WITH list
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index cfc4f5a9a72..270a945cc9b 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -689,9 +689,9 @@ typedef struct SubLink
* The values are assigned to the global PARAM_EXEC params indexed by parParam
* (the parParam and args lists must have the same ordering). setParam is a
* list of the PARAM_EXEC params that are computed by the sub-select, if it
- * is an initplan; they are listed in order by sub-select output column
- * position. (parParam and setParam are integer Lists, not Bitmapsets,
- * because their ordering is significant.)
+ * is an initplan or MULTIEXPR plan; they are listed in order by sub-select
+ * output column position. (parParam and setParam are integer Lists, not
+ * Bitmapsets, because their ordering is significant.)
*
* Also, the planner computes startup and per-call costs for use of the
* SubPlan. Note that these include the cost of the subquery proper,
@@ -724,8 +724,8 @@ typedef struct SubPlan
/* Note: parallel_safe does not consider contents of testexpr or args */
/* Information for passing params into and out of the subselect: */
/* setParam and parParam are lists of integers (param IDs) */
- List *setParam; /* initplan subqueries have to set these
- * Params for parent plan */
+ List *setParam; /* initplan and MULTIEXPR subqueries have to
+ * set these Params for parent plan */
List *parParam; /* indices of input Params from parent plan */
List *args; /* exprs to pass as parParam values */
/* Estimated execution costs: */
diff --git a/src/include/optimizer/subselect.h b/src/include/optimizer/subselect.h
index 6f33578c8f4..d6a872bd2c3 100644
--- a/src/include/optimizer/subselect.h
+++ b/src/include/optimizer/subselect.h
@@ -16,7 +16,6 @@
#include "nodes/pathnodes.h"
#include "nodes/plannodes.h"
-extern void SS_make_multiexprs_unique(PlannerInfo *root, PlannerInfo *subroot);
extern void SS_process_ctes(PlannerInfo *root);
extern JoinExpr *convert_ANY_sublink_to_join(PlannerInfo *root,
SubLink *sublink,
diff --git a/src/test/regress/expected/inherit.out b/src/test/regress/expected/inherit.out
index 658fed79226..cb61d75b9ed 100644
--- a/src/test/regress/expected/inherit.out
+++ b/src/test/regress/expected/inherit.out
@@ -1717,74 +1717,110 @@ reset enable_bitmapscan;
--
-- Check handling of MULTIEXPR SubPlans in inherited updates
--
-create table inhpar(f1 int, f2 text[], f3 int);
-insert into inhpar select generate_series(1,10);
-create table inhcld() inherits(inhpar);
-insert into inhcld select generate_series(11,10000);
-vacuum analyze inhcld;
-vacuum analyze inhpar;
+create table inhpar(f1 int, f2 name);
+create table inhcld(f2 name, f1 int);
+alter table inhcld inherit inhpar;
+insert into inhpar select x, x::text from generate_series(1,5) x;
+insert into inhcld select x::text, x from generate_series(6,10) x;
explain (verbose, costs off)
-update inhpar set
- (f1, f2[1]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
- (f2[2], f2[3]) = (select 'x', 'y' from int4_tbl limit 1),
- (f3, f2[4]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
- (f2[5], f2[6]) = (select 'x', 'y' from int4_tbl limit 1)
-from onek p2 where inhpar.f1 = p2.unique1;
- QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- Update on public.inhpar
- Update on public.inhpar
- Update on public.inhcld inhpar_1
- InitPlan 2 (returns $4,$5)
- -> Limit
- Output: 'x'::text, 'y'::text
- -> Seq Scan on public.int4_tbl int4_tbl_1
- Output: 'x'::text, 'y'::text
- InitPlan 4 (returns $10,$11)
- -> Limit
- Output: 'x'::text, 'y'::text
- -> Seq Scan on public.int4_tbl int4_tbl_3
- Output: 'x'::text, 'y'::text
- -> Merge Join
- Output: $12, (((((inhpar.f2[1] := $13)[2] := $4)[3] := $5)[4] := $15)[5] := $10)[6] := $11, $14, (SubPlan 1 (returns $2,$3)), NULL::record, (SubPlan 3 (returns $8,$9)), NULL::record, inhpar.ctid, p2.ctid
- Merge Cond: (p2.unique1 = inhpar.f1)
- -> Index Scan using onek_unique1 on public.onek p2
- Output: p2.unique2, p2.stringu1, p2.ctid, p2.unique1
- -> Sort
- Output: inhpar.f2, inhpar.ctid, inhpar.f1
- Sort Key: inhpar.f1
- -> Seq Scan on public.inhpar
- Output: inhpar.f2, inhpar.ctid, inhpar.f1
+update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1);
+ QUERY PLAN
+-----------------------------------------------------------------
+ Update on public.inhpar i
+ Update on public.inhpar i
+ Update on public.inhcld i_1
+ -> Seq Scan on public.inhpar i
+ Output: $2, $3, (SubPlan 1 (returns $2,$3)), i.ctid
SubPlan 1 (returns $2,$3)
-> Limit
- Output: (p2.unique2), (p2.stringu1)
+ Output: (i.f1), (((i.f2)::text || '-'::text))
-> Seq Scan on public.int4_tbl
- Output: p2.unique2, p2.stringu1
- SubPlan 3 (returns $8,$9)
- -> Limit
- Output: (p2.unique2), (p2.stringu1)
- -> Seq Scan on public.int4_tbl int4_tbl_2
- Output: p2.unique2, p2.stringu1
- -> Hash Join
- Output: $16, (((((inhpar_1.f2[1] := $17)[2] := $4)[3] := $5)[4] := $19)[5] := $10)[6] := $11, $18, (SubPlan 1 (returns $2,$3)), NULL::record, (SubPlan 3 (returns $8,$9)), NULL::record, inhpar_1.ctid, p2.ctid
- Hash Cond: (inhpar_1.f1 = p2.unique1)
- -> Seq Scan on public.inhcld inhpar_1
- Output: inhpar_1.f2, inhpar_1.ctid, inhpar_1.f1
- -> Hash
- Output: p2.unique2, p2.stringu1, p2.ctid, p2.unique1
- -> Seq Scan on public.onek p2
- Output: p2.unique2, p2.stringu1, p2.ctid, p2.unique1
-(42 rows)
-
-update inhpar set
- (f1, f2[1]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
- (f2[2], f2[3]) = (select 'x', 'y' from int4_tbl limit 1),
- (f3, f2[4]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
- (f2[5], f2[6]) = (select 'x', 'y' from int4_tbl limit 1)
-from onek p2 where inhpar.f1 = p2.unique1;
+ Output: i.f1, ((i.f2)::text || '-'::text)
+ -> Seq Scan on public.inhcld i_1
+ Output: $3, $2, (SubPlan 1 (returns $2,$3)), i_1.ctid
+(12 rows)
+
+update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1);
+select * from inhpar;
+ f1 | f2
+----+-----
+ 1 | 1-
+ 2 | 2-
+ 3 | 3-
+ 4 | 4-
+ 5 | 5-
+ 6 | 6-
+ 7 | 7-
+ 8 | 8-
+ 9 | 9-
+ 10 | 10-
+(10 rows)
+
drop table inhpar cascade;
NOTICE: drop cascades to table inhcld
--
+-- And the same for partitioned cases
+--
+create table inhpar(f1 int primary key, f2 name) partition by range (f1);
+create table inhcld1(f2 name, f1 int primary key);
+create table inhcld2(f1 int primary key, f2 name);
+alter table inhpar attach partition inhcld1 for values from (1) to (5);
+alter table inhpar attach partition inhcld2 for values from (5) to (100);
+insert into inhpar select x, x::text from generate_series(1,10) x;
+explain (verbose, costs off)
+update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1);
+ QUERY PLAN
+---------------------------------------------------------------------
+ Update on public.inhpar i
+ Update on public.inhcld1 i_1
+ Update on public.inhcld2 i_2
+ -> Seq Scan on public.inhcld1 i_1
+ Output: $3, $2, (SubPlan 1 (returns $2,$3)), i_1.ctid
+ SubPlan 1 (returns $2,$3)
+ -> Limit
+ Output: (i_1.f1), (((i_1.f2)::text || '-'::text))
+ -> Seq Scan on public.int4_tbl
+ Output: i_1.f1, ((i_1.f2)::text || '-'::text)
+ -> Seq Scan on public.inhcld2 i_2
+ Output: $2, $3, (SubPlan 1 (returns $2,$3)), i_2.ctid
+(12 rows)
+
+update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1);
+select * from inhpar;
+ f1 | f2
+----+-----
+ 1 | 1-
+ 2 | 2-
+ 3 | 3-
+ 4 | 4-
+ 5 | 5-
+ 6 | 6-
+ 7 | 7-
+ 8 | 8-
+ 9 | 9-
+ 10 | 10-
+(10 rows)
+
+-- Also check ON CONFLICT
+insert into inhpar as i values (3), (7) on conflict (f1)
+ do update set (f1, f2) = (select i.f1, i.f2 || '+');
+select * from inhpar order by f1; -- tuple order might be unstable here
+ f1 | f2
+----+-----
+ 1 | 1-
+ 2 | 2-
+ 3 | 3-+
+ 4 | 4-
+ 5 | 5-
+ 6 | 6-
+ 7 | 7-+
+ 8 | 8-
+ 9 | 9-
+ 10 | 10-
+(10 rows)
+
+drop table inhpar cascade;
+--
-- Check handling of a constant-null CHECK constraint
--
create table cnullparent (f1 int);
diff --git a/src/test/regress/sql/inherit.sql b/src/test/regress/sql/inherit.sql
index a4a33a3da8b..1aa59e8355d 100644
--- a/src/test/regress/sql/inherit.sql
+++ b/src/test/regress/sql/inherit.sql
@@ -632,26 +632,38 @@ reset enable_bitmapscan;
--
-- Check handling of MULTIEXPR SubPlans in inherited updates
--
-create table inhpar(f1 int, f2 text[], f3 int);
-insert into inhpar select generate_series(1,10);
-create table inhcld() inherits(inhpar);
-insert into inhcld select generate_series(11,10000);
-vacuum analyze inhcld;
-vacuum analyze inhpar;
+create table inhpar(f1 int, f2 name);
+create table inhcld(f2 name, f1 int);
+alter table inhcld inherit inhpar;
+insert into inhpar select x, x::text from generate_series(1,5) x;
+insert into inhcld select x::text, x from generate_series(6,10) x;
explain (verbose, costs off)
-update inhpar set
- (f1, f2[1]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
- (f2[2], f2[3]) = (select 'x', 'y' from int4_tbl limit 1),
- (f3, f2[4]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
- (f2[5], f2[6]) = (select 'x', 'y' from int4_tbl limit 1)
-from onek p2 where inhpar.f1 = p2.unique1;
-update inhpar set
- (f1, f2[1]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
- (f2[2], f2[3]) = (select 'x', 'y' from int4_tbl limit 1),
- (f3, f2[4]) = (select p2.unique2, p2.stringu1 from int4_tbl limit 1),
- (f2[5], f2[6]) = (select 'x', 'y' from int4_tbl limit 1)
-from onek p2 where inhpar.f1 = p2.unique1;
+update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1);
+update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1);
+select * from inhpar;
+
+drop table inhpar cascade;
+
+--
+-- And the same for partitioned cases
+--
+create table inhpar(f1 int primary key, f2 name) partition by range (f1);
+create table inhcld1(f2 name, f1 int primary key);
+create table inhcld2(f1 int primary key, f2 name);
+alter table inhpar attach partition inhcld1 for values from (1) to (5);
+alter table inhpar attach partition inhcld2 for values from (5) to (100);
+insert into inhpar select x, x::text from generate_series(1,10) x;
+
+explain (verbose, costs off)
+update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1);
+update inhpar i set (f1, f2) = (select i.f1, i.f2 || '-' from int4_tbl limit 1);
+select * from inhpar;
+
+-- Also check ON CONFLICT
+insert into inhpar as i values (3), (7) on conflict (f1)
+ do update set (f1, f2) = (select i.f1, i.f2 || '+');
+select * from inhpar order by f1; -- tuple order might be unstable here
drop table inhpar cascade;