aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/backend/nodes/outfuncs.c1
-rw-r--r--src/backend/optimizer/plan/createplan.c10
-rw-r--r--src/backend/optimizer/plan/planagg.c43
-rw-r--r--src/backend/optimizer/plan/planner.c37
-rw-r--r--src/backend/optimizer/plan/subselect.c266
-rw-r--r--src/backend/optimizer/prep/prepjointree.c1
-rw-r--r--src/include/nodes/relation.h7
-rw-r--r--src/include/optimizer/subselect.h8
-rw-r--r--src/test/regress/expected/join.out57
-rw-r--r--src/test/regress/sql/join.sql21
10 files changed, 293 insertions, 158 deletions
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 76091834e70..a87849805c6 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 f461586e08c..404c6f593d7 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 f0e9c05a452..a761cfdb09b 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);
@@ -578,23 +588,30 @@ make_agg_subplan(PlannerInfo *root, MinMaxAggInfo *mminfo)
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 09d4ea12e87..d598c1bd5c0 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 f3038cdffda..d0bc412c833 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,48 +2098,42 @@ 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;
@@ -2168,16 +2141,35 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan, bool attach_initplans)
}
/*
+ * 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;
@@ -2197,6 +2192,29 @@ finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
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
* way. We want the paramids set to include params in subplans as well as
@@ -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,13 +2735,12 @@ 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)
{
@@ -2720,24 +2748,10 @@ SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
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 9bf1c662b53..401ba5ba735 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 cb916ea8e14..5dc23d995f4 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 c609ac32af6..c3b1c79221b 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 a405a721e4e..cd4713f5e17 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 793eccf9faa..2b9bd20bc6a 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;