diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 76091834e7025ba99de56fb936530daca0528047..a87849805c6f5d726080e90b07153b80021ea570 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1799,6 +1799,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node) WRITE_NODE_FIELD(glob); WRITE_UINT_FIELD(query_level); WRITE_NODE_FIELD(plan_params); + WRITE_BITMAPSET_FIELD(outer_params); WRITE_BITMAPSET_FIELD(all_baserels); WRITE_BITMAPSET_FIELD(nullable_baserels); WRITE_NODE_FIELD(join_rel_list); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index f461586e08c5b3a2711eb55c003c26d2907388c7..404c6f593d7e5150dae984df348cf3a253c0206c 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -4473,11 +4473,7 @@ make_material(Plan *lefttree) * materialize_finished_plan: stick a Material node atop a completed plan * * There are a couple of places where we want to attach a Material node - * after completion of subquery_planner(). This currently requires hackery. - * Since subquery_planner has already run SS_finalize_plan on the subplan - * tree, we have to kluge up parameter lists for the Material node. - * Possibly this could be fixed by postponing SS_finalize_plan processing - * until setrefs.c is run? + * after completion of subquery_planner(), without any MaterialPath path. */ Plan * materialize_finished_plan(Plan *subplan) @@ -4498,10 +4494,6 @@ materialize_finished_plan(Plan *subplan) matplan->plan_rows = subplan->plan_rows; matplan->plan_width = subplan->plan_width; - /* parameter kluge --- see comments above */ - matplan->extParam = bms_copy(subplan->extParam); - matplan->allParam = bms_copy(subplan->allParam); - return matplan; } diff --git a/src/backend/optimizer/plan/planagg.c b/src/backend/optimizer/plan/planagg.c index f0e9c05a4520355a0592e954edbab68b96ec53fa..a761cfdb09b09ed7ba641a64e5e58e41a9a4be6c 100644 --- a/src/backend/optimizer/plan/planagg.c +++ b/src/backend/optimizer/plan/planagg.c @@ -416,13 +416,23 @@ build_minmax_path(PlannerInfo *root, MinMaxAggInfo *mminfo, * WHERE col IS NOT NULL AND existing-quals * ORDER BY col ASC/DESC * LIMIT 1) + * + * We cheat a bit here by building what is effectively a subplan query + * level without taking the trouble to increment varlevelsup of outer + * references. Therefore we don't increment the subroot's query_level nor + * repoint its parent_root to the parent level. We can get away with that + * because the plan will be an initplan and therefore cannot need any + * parameters from the parent level. But see hackery in make_agg_subplan; + * we might someday need to do this the hard way. *---------- */ subroot = (PlannerInfo *) palloc(sizeof(PlannerInfo)); memcpy(subroot, root, sizeof(PlannerInfo)); subroot->parse = parse = (Query *) copyObject(root->parse); - /* make sure subroot planning won't change root->init_plans contents */ - subroot->init_plans = list_copy(root->init_plans); + /* reset subplan-related stuff */ + subroot->plan_params = NIL; + subroot->outer_params = NULL; + subroot->init_plans = NIL; /* There shouldn't be any OJ or LATERAL info to translate, as yet */ Assert(subroot->join_info_list == NIL); Assert(subroot->lateral_info_list == NIL); @@ -577,24 +587,31 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *mminfo) subparse->limitCount, 0, 1); + /* + * We have to do some of the same cleanup that subquery_planner() would + * do, namely cope with params and initplans used within this plan tree. + * + * This is a little bit messy because although we initially created the + * subroot by cloning the outer root, it really is a subplan and needs to + * consider initplans belonging to the outer root as providing available + * parameters. So temporarily change its parent_root pointer. + * (Fortunately, SS_identify_outer_params doesn't care whether the depth + * of parent_root nesting matches query_level.) + */ + subroot->parent_root = root; + SS_identify_outer_params(subroot); + subroot->parent_root = root->parent_root; + + SS_attach_initplans(subroot, plan); + /* * Convert the plan into an InitPlan, and make a Param for its result. */ mminfo->param = - SS_make_initplan_from_plan(subroot, plan, + SS_make_initplan_from_plan(root, subroot, plan, exprType((Node *) mminfo->target), -1, exprCollation((Node *) mminfo->target)); - - /* - * Make sure the initplan gets into the outer PlannerInfo, along with any - * other initplans generated by the sub-planning run. We had to include - * the outer PlannerInfo's pre-existing initplans into the inner one's - * init_plans list earlier, so make sure we don't put back any duplicate - * entries. - */ - root->init_plans = list_concat_unique_ptr(root->init_plans, - subroot->init_plans); } /* diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 09d4ea12e874c6ae29f1a96405e430e686a97e0d..d598c1bd5c002e00cb0fdda01855942d2ef72749 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -239,6 +239,25 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) top_plan = materialize_finished_plan(top_plan); } + /* + * If any Params were generated, run through the plan tree and compute + * each plan node's extParam/allParam sets. Ideally we'd merge this into + * set_plan_references' tree traversal, but for now it has to be separate + * because we need to visit subplans before not after main plan. + */ + if (glob->nParamExec > 0) + { + Assert(list_length(glob->subplans) == list_length(glob->subroots)); + forboth(lp, glob->subplans, lr, glob->subroots) + { + Plan *subplan = (Plan *) lfirst(lp); + PlannerInfo *subroot = (PlannerInfo *) lfirst(lr); + + SS_finalize_plan(subroot, subplan); + } + SS_finalize_plan(root, top_plan); + } + /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); Assert(glob->finalrowmarks == NIL); @@ -312,7 +331,6 @@ subquery_planner(PlannerGlobal *glob, Query *parse, bool hasRecursion, double tuple_fraction, PlannerInfo **subroot) { - int num_old_subplans = list_length(glob->subplans); PlannerInfo *root; Plan *plan; List *newWithCheckOptions; @@ -327,6 +345,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, root->query_level = parent_root ? parent_root->query_level + 1 : 1; root->parent_root = parent_root; root->plan_params = NIL; + root->outer_params = NULL; root->planner_cxt = CurrentMemoryContext; root->init_plans = NIL; root->cte_plan_ids = NIL; @@ -656,13 +675,17 @@ subquery_planner(PlannerGlobal *glob, Query *parse, } /* - * If any subplans were generated, or if there are any parameters to worry - * about, build initPlan list and extParam/allParam sets for plan nodes, - * and attach the initPlans to the top plan node. + * Capture the set of outer-level param IDs we have access to, for use in + * extParam/allParam calculations later. + */ + SS_identify_outer_params(root); + + /* + * If any initPlans were created in this query level, attach them to the + * topmost plan node for the level, and increment that node's cost to + * account for them. */ - if (list_length(glob->subplans) != num_old_subplans || - root->glob->nParamExec > 0) - SS_finalize_plan(root, plan, true); + SS_attach_initplans(root, plan); /* Return internal info if caller wants it */ if (subroot) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index f3038cdffda3ad9467935327df6c1cf7913798f1..d0bc412c8339773c68d5dac53f975cb298951d9c 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -22,6 +22,7 @@ #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/pathnode.h" #include "optimizer/planmain.h" #include "optimizer/planner.h" #include "optimizer/prep.h" @@ -2048,60 +2049,38 @@ process_sublinks_mutator(Node *node, process_sublinks_context *context) } /* - * SS_finalize_plan - do final sublink and parameter processing for a - * completed Plan. + * SS_identify_outer_params - identify the Params available from outer levels * - * This recursively computes the extParam and allParam sets for every Plan - * node in the given plan tree. It also optionally attaches any previously - * generated InitPlans to the top plan node. (Any InitPlans should already - * have been put through SS_finalize_plan.) + * This must be run after SS_replace_correlation_vars and SS_process_sublinks + * processing is complete in a given query level as well as all of its + * descendant levels (which means it's most practical to do it at the end of + * processing the query level). We compute the set of paramIds that outer + * levels will make available to this level+descendants, and record it in + * root->outer_params for use while computing extParam/allParam sets in final + * plan cleanup. (We can't just compute it then, because the upper levels' + * plan_params lists are transient and will be gone by then.) */ void -SS_finalize_plan(PlannerInfo *root, Plan *plan, bool attach_initplans) +SS_identify_outer_params(PlannerInfo *root) { - Bitmapset *valid_params, - *initExtParam, - *initSetParam; - Cost initplan_cost; + Bitmapset *outer_params; PlannerInfo *proot; ListCell *l; /* - * Examine any initPlans to determine the set of external params they - * reference, the set of output params they supply, and their total cost. - * We'll use at least some of this info below. (Note we are assuming that - * finalize_plan doesn't touch the initPlans.) - * - * In the case where attach_initplans is false, we are assuming that the - * existing initPlans are siblings that might supply params needed by the - * current plan. + * If no parameters have been assigned anywhere in the tree, we certainly + * don't need to do anything here. */ - initExtParam = initSetParam = NULL; - initplan_cost = 0; - foreach(l, root->init_plans) - { - SubPlan *initsubplan = (SubPlan *) lfirst(l); - Plan *initplan = planner_subplan_get_plan(root, initsubplan); - ListCell *l2; - - initExtParam = bms_add_members(initExtParam, initplan->extParam); - foreach(l2, initsubplan->setParam) - { - initSetParam = bms_add_member(initSetParam, lfirst_int(l2)); - } - initplan_cost += initsubplan->startup_cost + initsubplan->per_call_cost; - } + if (root->glob->nParamExec == 0) + return; /* - * Now determine the set of params that are validly referenceable in this - * query level; to wit, those available from outer query levels plus the - * output parameters of any local initPlans. (We do not include output - * parameters of regular subplans. Those should only appear within the - * testexpr of SubPlan nodes, and are taken care of locally within - * finalize_primnode. Likewise, special parameters that are generated by - * nodes such as ModifyTable are handled within finalize_plan.) + * Scan all query levels above this one to see which parameters are due to + * be available from them, either because lower query levels have + * requested them (via plan_params) or because they will be available from + * initPlans of those levels. */ - valid_params = bms_copy(initSetParam); + outer_params = NULL; for (proot = root->parent_root; proot != NULL; proot = proot->parent_root) { /* Include ordinary Var/PHV/Aggref params */ @@ -2109,7 +2088,7 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan, bool attach_initplans) { PlannerParamItem *pitem = (PlannerParamItem *) lfirst(l); - valid_params = bms_add_member(valid_params, pitem->paramId); + outer_params = bms_add_member(outer_params, pitem->paramId); } /* Include any outputs of outer-level initPlans */ foreach(l, proot->init_plans) @@ -2119,65 +2098,78 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan, bool attach_initplans) foreach(l2, initsubplan->setParam) { - valid_params = bms_add_member(valid_params, lfirst_int(l2)); + outer_params = bms_add_member(outer_params, lfirst_int(l2)); } } /* Include worktable ID, if a recursive query is being planned */ if (proot->wt_param_id >= 0) - valid_params = bms_add_member(valid_params, proot->wt_param_id); + outer_params = bms_add_member(outer_params, proot->wt_param_id); } + root->outer_params = outer_params; +} - /* - * Now recurse through plan tree. - */ - (void) finalize_plan(root, plan, valid_params, NULL); - - bms_free(valid_params); +/* + * SS_attach_initplans - attach initplans to topmost plan node + * + * Attach any initplans created in the current query level to the topmost plan + * node for the query level, and increment that node's cost to account for + * them. (The initPlans could actually go in any node at or above where + * they're referenced, but there seems no reason to put them any lower than + * the topmost node for the query level.) + */ +void +SS_attach_initplans(PlannerInfo *root, Plan *plan) +{ + ListCell *lc; - /* - * Finally, attach any initPlans to the topmost plan node, and add their - * extParams to the topmost node's, too. However, any setParams of the - * initPlans should not be present in the topmost node's extParams, only - * in its allParams. (As of PG 8.1, it's possible that some initPlans - * have extParams that are setParams of other initPlans, so we have to - * take care of this situation explicitly.) - * - * We also add the eval cost of each initPlan to the startup cost of the - * top node. This is a conservative overestimate, since in fact each - * initPlan might be executed later than plan startup, or even not at all. - */ - if (attach_initplans) + plan->initPlan = root->init_plans; + foreach(lc, plan->initPlan) { - plan->initPlan = root->init_plans; - root->init_plans = NIL; /* make sure they're not attached twice */ - - /* allParam must include all these params */ - plan->allParam = bms_add_members(plan->allParam, initExtParam); - plan->allParam = bms_add_members(plan->allParam, initSetParam); - /* extParam must include any child extParam */ - plan->extParam = bms_add_members(plan->extParam, initExtParam); - /* but extParam shouldn't include any setParams */ - plan->extParam = bms_del_members(plan->extParam, initSetParam); - /* ensure extParam is exactly NULL if it's empty */ - if (bms_is_empty(plan->extParam)) - plan->extParam = NULL; + SubPlan *initsubplan = (SubPlan *) lfirst(lc); + Cost initplan_cost; + + /* + * Assume each initPlan gets run once during top plan startup. This + * is a conservative overestimate, since in fact an initPlan might be + * executed later than plan startup, or even not at all. + */ + initplan_cost = initsubplan->startup_cost + initsubplan->per_call_cost; plan->startup_cost += initplan_cost; plan->total_cost += initplan_cost; } } +/* + * SS_finalize_plan - do final parameter processing for a completed Plan. + * + * This recursively computes the extParam and allParam sets for every Plan + * node in the given plan tree. (Oh, and RangeTblFunction.funcparams too.) + * + * We assume that SS_finalize_plan has already been run on any initplans or + * subplans the plan tree could reference. + */ +void +SS_finalize_plan(PlannerInfo *root, Plan *plan) +{ + /* No setup needed, just recurse through plan tree. */ + (void) finalize_plan(root, plan, root->outer_params, NULL); +} + /* * Recursive processing of all nodes in the plan tree * - * valid_params is the set of param IDs considered valid to reference in - * this plan node or its children. + * valid_params is the set of param IDs supplied by outer plan levels + * that are valid to reference in this plan node or its children. + * * scan_params is a set of param IDs to force scan plan nodes to reference. * This is for EvalPlanQual support, and is always NULL at the top of the * recursion. * * The return value is the computed allParam set for the given Plan node. * This is just an internal notational convenience. + * + * Do not scribble on caller's values of valid_params or scan_params! */ static Bitmapset * finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, @@ -2186,7 +2178,10 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, finalize_primnode_context context; int locally_added_param; Bitmapset *nestloop_params; + Bitmapset *initExtParam; + Bitmapset *initSetParam; Bitmapset *child_params; + ListCell *l; if (plan == NULL) return NULL; @@ -2196,6 +2191,29 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, locally_added_param = -1; /* there isn't one */ nestloop_params = NULL; /* there aren't any */ + /* + * Examine any initPlans to determine the set of external params they + * reference and the set of output params they supply. (We assume + * SS_finalize_plan was run on them already.) + */ + initExtParam = initSetParam = NULL; + foreach(l, plan->initPlan) + { + SubPlan *initsubplan = (SubPlan *) lfirst(l); + Plan *initplan = planner_subplan_get_plan(root, initsubplan); + ListCell *l2; + + initExtParam = bms_add_members(initExtParam, initplan->extParam); + foreach(l2, initsubplan->setParam) + { + initSetParam = bms_add_member(initSetParam, lfirst_int(l2)); + } + } + + /* Any setParams are validly referenceable in this node and children */ + if (initSetParam) + valid_params = bms_union(valid_params, initSetParam); + /* * When we call finalize_primnode, context.paramids sets are automatically * merged together. But when recursing to self, we have to do it the hard @@ -2274,18 +2292,22 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, break; case T_SubqueryScan: + { + SubqueryScan *sscan = (SubqueryScan *) plan; + RelOptInfo *rel; - /* - * In a SubqueryScan, SS_finalize_plan has already been run on the - * subplan by the inner invocation of subquery_planner, so there's - * no need to do it again. Instead, just pull out the subplan's - * extParams list, which represents the params it needs from my - * level and higher levels. - */ - context.paramids = bms_add_members(context.paramids, - ((SubqueryScan *) plan)->subplan->extParam); - /* We need scan_params too, though */ - context.paramids = bms_add_members(context.paramids, scan_params); + /* We must run SS_finalize_plan on the subquery */ + rel = find_base_rel(root, sscan->scan.scanrelid); + Assert(rel->subplan == sscan->subplan); + SS_finalize_plan(rel->subroot, sscan->subplan); + + /* Now we can add its extParams to the parent's params */ + context.paramids = bms_add_members(context.paramids, + sscan->subplan->extParam); + /* We need scan_params too, though */ + context.paramids = bms_add_members(context.paramids, + scan_params); + } break; case T_FunctionScan: @@ -2338,7 +2360,8 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, * have to do instead is to find the referenced CTE plan and * incorporate its external paramids, so that the correct * things will happen if the CTE references outer-level - * variables. See test cases for bug #4902. + * variables. See test cases for bug #4902. (We assume + * SS_finalize_plan was run on the CTE plan already.) */ int plan_id = ((CteScan *) plan)->ctePlanId; Plan *cteplan; @@ -2610,30 +2633,35 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params, locally_added_param); } - /* Now we have all the paramids */ + /* Now we have all the paramids referenced in this node and children */ if (!bms_is_subset(context.paramids, valid_params)) elog(ERROR, "plan should not reference subplan's variable"); /* - * Note: by definition, extParam and allParam should have the same value - * in any plan node that doesn't have child initPlans. We set them equal - * here, and later SS_finalize_plan will update them properly in node(s) - * that it attaches initPlans to. - * + * The plan node's allParam and extParam fields should include all its + * referenced paramids, plus contributions from any child initPlans. + * However, any setParams of the initPlans should not be present in the + * parent node's extParams, only in its allParams. (It's possible that + * some initPlans have extParams that are setParams of other initPlans.) + */ + + /* allParam must include initplans' extParams and setParams */ + plan->allParam = bms_union(context.paramids, initExtParam); + plan->allParam = bms_add_members(plan->allParam, initSetParam); + /* extParam must include any initplan extParams */ + plan->extParam = bms_union(context.paramids, initExtParam); + /* but not any initplan setParams */ + plan->extParam = bms_del_members(plan->extParam, initSetParam); + + /* * For speed at execution time, make sure extParam/allParam are actually * NULL if they are empty sets. */ - if (bms_is_empty(context.paramids)) - { + if (bms_is_empty(plan->extParam)) plan->extParam = NULL; + if (bms_is_empty(plan->allParam)) plan->allParam = NULL; - } - else - { - plan->extParam = context.paramids; - plan->allParam = bms_copy(context.paramids); - } return plan->allParam; } @@ -2686,7 +2714,8 @@ finalize_primnode(Node *node, finalize_primnode_context *context) /* * Add params needed by the subplan to paramids, but excluding those - * we will pass down to it. + * we will pass down to it. (We assume SS_finalize_plan was run on + * the subplan already.) */ subparamids = bms_copy(plan->extParam); foreach(lc, subplan->parParam) @@ -2706,38 +2735,23 @@ finalize_primnode(Node *node, finalize_primnode_context *context) * * The plan is expected to return a scalar value of the given type/collation. * We build an EXPR_SUBLINK SubPlan node and put it into the initplan - * list for the current query level. A Param that represents the initplan's + * list for the outer query level. A Param that represents the initplan's * output is returned. - * - * We assume the plan hasn't been put through SS_finalize_plan. */ Param * -SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan, +SS_make_initplan_from_plan(PlannerInfo *root, + PlannerInfo *subroot, Plan *plan, Oid resulttype, int32 resulttypmod, Oid resultcollation) { SubPlan *node; Param *prm; - /* - * We must run SS_finalize_plan(), since that's normally done before a - * subplan gets put into the initplan list. Tell it not to attach any - * pre-existing initplans to this one, since they are siblings not - * children of this initplan. (This is something else that could perhaps - * be cleaner if we did extParam/allParam processing in setrefs.c instead - * of here? See notes for materialize_finished_plan.) - */ - - /* - * Build extParam/allParam sets for plan nodes. - */ - SS_finalize_plan(root, plan, false); - /* * Add the subplan and its PlannerInfo to the global lists. */ root->glob->subplans = lappend(root->glob->subplans, plan); - root->glob->subroots = lappend(root->glob->subroots, root); + root->glob->subroots = lappend(root->glob->subroots, subroot); /* * Create a SubPlan node and add it to the outer list of InitPlans. Note @@ -2757,7 +2771,7 @@ SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan, * parParam and args lists remain empty. */ - cost_subplan(root, node, plan); + cost_subplan(subroot, node, plan); /* * Make a Param that will be the subplan's output. diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 9bf1c662b5371614dabb5b39dd90a5f676f3678f..401ba5ba735834b7ba6c6fffa7a01ba9a6304280 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -899,6 +899,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, subroot->query_level = root->query_level; subroot->parent_root = root->parent_root; subroot->plan_params = NIL; + subroot->outer_params = NULL; subroot->planner_cxt = CurrentMemoryContext; subroot->init_plans = NIL; subroot->cte_plan_ids = NIL; diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index cb916ea8e14e923fb772334b40bf3f166ca5e7d6..5dc23d995f40e3edcf0ccdddee2afc089f5b039d 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -131,7 +131,14 @@ typedef struct PlannerInfo struct PlannerInfo *parent_root; /* NULL at outermost Query */ + /* + * plan_params contains the expressions that this query level needs to + * make available to a lower query level that is currently being planned. + * outer_params contains the paramIds of PARAM_EXEC Params that outer + * query levels will make available to this query level. + */ List *plan_params; /* list of PlannerParamItems, see below */ + Bitmapset *outer_params; /* * simple_rel_array holds pointers to "base rels" and "other rels" (see diff --git a/src/include/optimizer/subselect.h b/src/include/optimizer/subselect.h index c609ac32af679a891208d3e0e5531bbec63dda59..c3b1c79221bb9cfc1885a1ce7d810ccd6ade6914 100644 --- a/src/include/optimizer/subselect.h +++ b/src/include/optimizer/subselect.h @@ -25,9 +25,11 @@ extern JoinExpr *convert_EXISTS_sublink_to_join(PlannerInfo *root, Relids available_rels); extern Node *SS_replace_correlation_vars(PlannerInfo *root, Node *expr); extern Node *SS_process_sublinks(PlannerInfo *root, Node *expr, bool isQual); -extern void SS_finalize_plan(PlannerInfo *root, Plan *plan, - bool attach_initplans); -extern Param *SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan, +extern void SS_identify_outer_params(PlannerInfo *root); +extern void SS_attach_initplans(PlannerInfo *root, Plan *plan); +extern void SS_finalize_plan(PlannerInfo *root, Plan *plan); +extern Param *SS_make_initplan_from_plan(PlannerInfo *root, + PlannerInfo *subroot, Plan *plan, Oid resulttype, int32 resulttypmod, Oid resultcollation); extern Param *assign_nestloop_param_var(PlannerInfo *root, Var *var); extern Param *assign_nestloop_param_placeholdervar(PlannerInfo *root, diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index a405a721e4e76206909fdbc6113a11b6b84086f3..cd4713f5e1723f589c1d616ff86db0c433e1a8bf 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -4889,6 +4889,63 @@ select * from 0 | 9998 | 0 (1 row) +-- check proper extParam/allParam handling (this isn't exactly a LATERAL issue, +-- but we can make the test case much more compact with LATERAL) +explain (verbose, costs off) +select * from (values (0), (1)) v(id), +lateral (select * from int8_tbl t1, + lateral (select * from + (select * from int8_tbl t2 + where q1 = any (select q2 from int8_tbl t3 + where q2 = (select greatest(t1.q1,t2.q2)) + and (select v.id=0)) offset 0) ss2) ss + where t1.q1 = ss.q2) ss0; + QUERY PLAN +----------------------------------------------------------------- + Nested Loop + Output: "*VALUES*".column1, t1.q1, t1.q2, ss2.q1, ss2.q2 + -> Seq Scan on public.int8_tbl t1 + Output: t1.q1, t1.q2 + -> Nested Loop + Output: "*VALUES*".column1, ss2.q1, ss2.q2 + -> Values Scan on "*VALUES*" + Output: "*VALUES*".column1 + -> Subquery Scan on ss2 + Output: ss2.q1, ss2.q2 + Filter: (t1.q1 = ss2.q2) + -> Seq Scan on public.int8_tbl t2 + Output: t2.q1, t2.q2 + Filter: (SubPlan 3) + SubPlan 3 + -> Result + Output: t3.q2 + One-Time Filter: $4 + InitPlan 1 (returns $2) + -> Result + Output: GREATEST($0, t2.q2) + InitPlan 2 (returns $4) + -> Result + Output: ($3 = 0) + -> Seq Scan on public.int8_tbl t3 + Output: t3.q1, t3.q2 + Filter: (t3.q2 = $2) +(27 rows) + +select * from (values (0), (1)) v(id), +lateral (select * from int8_tbl t1, + lateral (select * from + (select * from int8_tbl t2 + where q1 = any (select q2 from int8_tbl t3 + where q2 = (select greatest(t1.q1,t2.q2)) + and (select v.id=0)) offset 0) ss2) ss + where t1.q1 = ss.q2) ss0; + id | q1 | q2 | q1 | q2 +----+------------------+-------------------+------------------+------------------ + 0 | 4567890123456789 | 123 | 4567890123456789 | 4567890123456789 + 0 | 4567890123456789 | 4567890123456789 | 4567890123456789 | 4567890123456789 + 0 | 4567890123456789 | -4567890123456789 | 4567890123456789 | 4567890123456789 +(3 rows) + -- test some error cases where LATERAL should have been used but wasn't select f1,g from int4_tbl a, (select f1 as g) ss; ERROR: column "f1" does not exist diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index 793eccf9faac1eb77fdd782fbcf2e43c1e5737e5..2b9bd20bc6afcc1be399e6c2f3df78007b7daef5 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -1542,6 +1542,27 @@ select * from where f1 = any (select unique1 from tenk1 where unique2 = v.x offset 0)) ss; +-- check proper extParam/allParam handling (this isn't exactly a LATERAL issue, +-- but we can make the test case much more compact with LATERAL) +explain (verbose, costs off) +select * from (values (0), (1)) v(id), +lateral (select * from int8_tbl t1, + lateral (select * from + (select * from int8_tbl t2 + where q1 = any (select q2 from int8_tbl t3 + where q2 = (select greatest(t1.q1,t2.q2)) + and (select v.id=0)) offset 0) ss2) ss + where t1.q1 = ss.q2) ss0; + +select * from (values (0), (1)) v(id), +lateral (select * from int8_tbl t1, + lateral (select * from + (select * from int8_tbl t2 + where q1 = any (select q2 from int8_tbl t3 + where q2 = (select greatest(t1.q1,t2.q2)) + and (select v.id=0)) offset 0) ss2) ss + where t1.q1 = ss.q2) ss0; + -- test some error cases where LATERAL should have been used but wasn't select f1,g from int4_tbl a, (select f1 as g) ss; select f1,g from int4_tbl a, (select a.f1 as g) ss;