Postpone extParam/allParam calculations until the very end of planning.

Until now we computed these Param ID sets at the end of subquery_planner,
but that approach depends on subquery_planner returning a concrete Plan
tree.  We would like to switch over to returning one or more Paths for a
subquery, and in that representation the necessary details aren't fully
fleshed out (not to mention that we don't really want to do this work for
Paths that end up getting discarded).  Hence, refactor so that we can
compute the param ID sets at the end of planning, just before
set_plan_references is run.

The main change necessary to make this work is that we need to capture
the set of outer-level Param IDs available to the current query level
before exiting subquery_planner, since the outer levels' plan_params lists
are transient.  (That's not going to pose a problem for returning Paths,
since all the work involved in producing that data is part of expression
preprocessing, which will continue to happen before Paths are produced.)
On the plus side, this change gets rid of several existing kluges.

Eventually I'd like to get rid of SS_finalize_plan altogether in favor of
doing this work during set_plan_references, but that will require some
complex rejiggering because SS_finalize_plan needs to visit subplans and
initplans before the main plan.  So leave that idea for another day.
This commit is contained in:
Tom Lane 2015-08-11 23:48:37 -04:00
parent 4901b2f495
commit 68fa28f771
10 changed files with 338 additions and 203 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}
/*

View File

@ -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.
*/
if (list_length(glob->subplans) != num_old_subplans ||
root->glob->nParamExec > 0)
SS_finalize_plan(root, plan, true);
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.
*/
SS_attach_initplans(root, plan);
/* Return internal info if caller wants it */
if (subroot)

View File

@ -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,37 +2049,155 @@ 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;
/*
* If no parameters have been assigned anywhere in the tree, we certainly
* don't need to do anything here.
*/
if (root->glob->nParamExec == 0)
return;
/*
* 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.
*/
outer_params = NULL;
for (proot = root->parent_root; proot != NULL; proot = proot->parent_root)
{
/* Include ordinary Var/PHV/Aggref params */
foreach(l, proot->plan_params)
{
PlannerParamItem *pitem = (PlannerParamItem *) lfirst(l);
outer_params = bms_add_member(outer_params, pitem->paramId);
}
/* Include any outputs of outer-level initPlans */
foreach(l, proot->init_plans)
{
SubPlan *initsubplan = (SubPlan *) lfirst(l);
ListCell *l2;
foreach(l2, initsubplan->setParam)
{
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)
outer_params = bms_add_member(outer_params, proot->wt_param_id);
}
root->outer_params = outer_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;
plan->initPlan = root->init_plans;
foreach(lc, plan->initPlan)
{
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 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,
Bitmapset *scan_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;
context.root = root;
context.paramids = NULL; /* initialize set to empty */
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, 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.
* reference and the set of output params they supply. (We assume
* SS_finalize_plan was run on them already.)
*/
initExtParam = initSetParam = NULL;
initplan_cost = 0;
foreach(l, root->init_plans)
foreach(l, plan->initPlan)
{
SubPlan *initsubplan = (SubPlan *) lfirst(l);
Plan *initplan = planner_subplan_get_plan(root, initsubplan);
@ -2089,112 +2208,11 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan, bool attach_initplans)
{
initSetParam = bms_add_member(initSetParam, lfirst_int(l2));
}
initplan_cost += initsubplan->startup_cost + initsubplan->per_call_cost;
}
/*
* 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.)
*/
valid_params = bms_copy(initSetParam);
for (proot = root->parent_root; proot != NULL; proot = proot->parent_root)
{
/* Include ordinary Var/PHV/Aggref params */
foreach(l, proot->plan_params)
{
PlannerParamItem *pitem = (PlannerParamItem *) lfirst(l);
valid_params = bms_add_member(valid_params, pitem->paramId);
}
/* Include any outputs of outer-level initPlans */
foreach(l, proot->init_plans)
{
SubPlan *initsubplan = (SubPlan *) lfirst(l);
ListCell *l2;
foreach(l2, initsubplan->setParam)
{
valid_params = bms_add_member(valid_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);
}
/*
* Now recurse through plan tree.
*/
(void) finalize_plan(root, plan, valid_params, NULL);
bms_free(valid_params);
/*
* 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;
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;
plan->startup_cost += initplan_cost;
plan->total_cost += initplan_cost;
}
}
/*
* 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.
* 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.
*/
static Bitmapset *
finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
Bitmapset *scan_params)
{
finalize_primnode_context context;
int locally_added_param;
Bitmapset *nestloop_params;
Bitmapset *child_params;
if (plan == NULL)
return NULL;
context.root = root;
context.paramids = NULL; /* initialize set to empty */
locally_added_param = -1; /* there isn't one */
nestloop_params = NULL; /* there aren't any */
/* 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
@ -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.

View File

@ -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;

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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;