diff options
-rw-r--r-- | src/backend/executor/execExpr.c | 149 | ||||
-rw-r--r-- | src/backend/executor/nodeSubplan.c | 134 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 4 | ||||
-rw-r--r-- | src/backend/optimizer/plan/subselect.c | 129 | ||||
-rw-r--r-- | src/include/nodes/primnodes.h | 10 | ||||
-rw-r--r-- | src/include/optimizer/subselect.h | 1 | ||||
-rw-r--r-- | src/test/regress/expected/inherit.out | 158 | ||||
-rw-r--r-- | src/test/regress/sql/inherit.sql | 48 |
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; |