1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-05 07:21:24 +03:00

Fix PARAM_EXEC assignment mechanism to be safe in the presence of WITH.

The planner previously assumed that parameter Vars having the same absolute
query level, varno, and varattno could safely be assigned the same runtime
PARAM_EXEC slot, even though they might be different Vars appearing in
different subqueries.  This was (probably) safe before the introduction of
CTEs, but the lazy-evalution mechanism used for CTEs means that a CTE can
be executed during execution of some other subquery, causing the lifespan
of Params at the same syntactic nesting level as the CTE to overlap with
use of the same slots inside the CTE.  In 9.1 we created additional hazards
by using the same parameter-assignment technology for nestloop inner scan
parameters, but it was broken before that, as illustrated by the added
regression test.

To fix, restructure the planner's management of PlannerParamItems so that
items having different semantic lifespans are kept rigorously separated.
This will probably result in complex queries using more runtime PARAM_EXEC
slots than before, but the slots are cheap enough that this hardly matters.
Also, stop generating PlannerParamItems containing Params for subquery
outputs: all we really need to do is reserve the PARAM_EXEC slot number,
and that now only takes incrementing a counter.  The planning code is
simpler and probably faster than before, as well as being more correct.

Per report from Vik Reykja.

These changes will mostly also need to be made in the back branches, but
I'm going to hold off on that until after 9.2.0 wraps.
This commit is contained in:
Tom Lane
2012-09-05 12:54:03 -04:00
parent e20a90e188
commit 46c508fbcf
11 changed files with 268 additions and 211 deletions

View File

@ -1145,6 +1145,9 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
else
tuple_fraction = root->tuple_fraction;
/* plan_params should not be in use in current query level */
Assert(root->plan_params == NIL);
/* Generate the plan for the subquery */
rel->subplan = subquery_planner(root->glob, subquery,
root,
@ -1152,6 +1155,10 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
&subroot);
rel->subroot = subroot;
/* Isolate the params needed by this specific subplan */
rel->subplan_params = root->plan_params;
root->plan_params = NIL;
/*
* It's possible that constraint exclusion proved the subquery empty. If
* so, it's convenient to turn it back into a dummy path so that we will

View File

@ -84,7 +84,8 @@ static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path,
Plan *outer_plan, Plan *inner_plan);
static Node *replace_nestloop_params(PlannerInfo *root, Node *expr);
static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root);
static void identify_nestloop_extparams(PlannerInfo *root, Plan *subplan);
static void process_subquery_nestloop_params(PlannerInfo *root,
List *subplan_params);
static List *fix_indexqual_references(PlannerInfo *root, IndexPath *index_path);
static List *fix_indexorderby_references(PlannerInfo *root, IndexPath *index_path);
static Node *fix_indexqual_operand(Node *node, IndexOptInfo *index, int indexcol);
@ -188,6 +189,9 @@ create_plan(PlannerInfo *root, Path *best_path)
{
Plan *plan;
/* plan_params should not be in use in current query level */
Assert(root->plan_params == NIL);
/* Initialize this module's private workspace in PlannerInfo */
root->curOuterRels = NULL;
root->curOuterParams = NIL;
@ -199,6 +203,12 @@ create_plan(PlannerInfo *root, Path *best_path)
if (root->curOuterParams != NIL)
elog(ERROR, "failed to assign all NestLoopParams to plan nodes");
/*
* Reset plan_params to ensure param IDs used for nestloop params are not
* re-used later
*/
root->plan_params = NIL;
return plan;
}
@ -1662,7 +1672,8 @@ create_subqueryscan_plan(PlannerInfo *root, Path *best_path,
{
scan_clauses = (List *)
replace_nestloop_params(root, (Node *) scan_clauses);
identify_nestloop_extparams(root, best_path->parent->subplan);
process_subquery_nestloop_params(root,
best_path->parent->subplan_params);
}
scan_plan = make_subqueryscan(tlist,
@ -2620,30 +2631,26 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root)
}
/*
* identify_nestloop_extparams
* Identify extParams of a parameterized subquery that need to be fed
* process_subquery_nestloop_params
* Handle params of a parameterized subquery that need to be fed
* from an outer nestloop.
*
* Currently, that would be *all* params that a subquery in FROM has demanded
* from the current query level, since they must be LATERAL references.
*
* The subplan's references to the outer variables are already represented
* as PARAM_EXEC Params, so we need not modify the subplan here. What we
* do need to do is add entries to root->curOuterParams to signal the parent
* nestloop plan node that it must provide these values.
*/
static void
identify_nestloop_extparams(PlannerInfo *root, Plan *subplan)
process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params)
{
Bitmapset *tmpset;
int paramid;
ListCell *ppl;
/* Examine each extParam of the subquery's plan */
tmpset = bms_copy(subplan->extParam);
while ((paramid = bms_first_member(tmpset)) >= 0)
foreach(ppl, subplan_params)
{
PlannerParamItem *pitem = list_nth(root->glob->paramlist, paramid);
/* Ignore anything coming from an upper query level */
if (pitem->abslevel != root->query_level)
continue;
PlannerParamItem *pitem = (PlannerParamItem *) lfirst(ppl);
if (IsA(pitem->item, Var))
{
@ -2651,14 +2658,14 @@ identify_nestloop_extparams(PlannerInfo *root, Plan *subplan)
NestLoopParam *nlp;
ListCell *lc;
/* If not from a nestloop outer rel, nothing to do */
/* If not from a nestloop outer rel, complain */
if (!bms_is_member(var->varno, root->curOuterRels))
continue;
elog(ERROR, "non-LATERAL parameter required by subquery");
/* Is this param already listed in root->curOuterParams? */
foreach(lc, root->curOuterParams)
{
nlp = (NestLoopParam *) lfirst(lc);
if (nlp->paramno == paramid)
if (nlp->paramno == pitem->paramId)
{
Assert(equal(var, nlp->paramval));
/* Present, so nothing to do */
@ -2669,7 +2676,7 @@ identify_nestloop_extparams(PlannerInfo *root, Plan *subplan)
{
/* No, so add it */
nlp = makeNode(NestLoopParam);
nlp->paramno = paramid;
nlp->paramno = pitem->paramId;
nlp->paramval = copyObject(var);
root->curOuterParams = lappend(root->curOuterParams, nlp);
}
@ -2680,22 +2687,15 @@ identify_nestloop_extparams(PlannerInfo *root, Plan *subplan)
NestLoopParam *nlp;
ListCell *lc;
/*
* If not from a nestloop outer rel, nothing to do. We use
* bms_overlap as a cheap/quick test to see if the PHV might be
* evaluated in the outer rels, and then grab its PlaceHolderInfo
* to tell for sure.
*/
if (!bms_overlap(phv->phrels, root->curOuterRels))
continue;
/* If not from a nestloop outer rel, complain */
if (!bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at,
root->curOuterRels))
continue;
elog(ERROR, "non-LATERAL parameter required by subquery");
/* Is this param already listed in root->curOuterParams? */
foreach(lc, root->curOuterParams)
{
nlp = (NestLoopParam *) lfirst(lc);
if (nlp->paramno == paramid)
if (nlp->paramno == pitem->paramId)
{
Assert(equal(phv, nlp->paramval));
/* Present, so nothing to do */
@ -2706,13 +2706,14 @@ identify_nestloop_extparams(PlannerInfo *root, Plan *subplan)
{
/* No, so add it */
nlp = makeNode(NestLoopParam);
nlp->paramno = paramid;
nlp->paramno = pitem->paramId;
nlp->paramval = copyObject(phv);
root->curOuterParams = lappend(root->curOuterParams, nlp);
}
}
else
elog(ERROR, "unexpected type of subquery parameter");
}
bms_free(tmpset);
}
/*

View File

@ -155,7 +155,6 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
glob = makeNode(PlannerGlobal);
glob->boundParams = boundParams;
glob->paramlist = NIL;
glob->subplans = NIL;
glob->subroots = NIL;
glob->rewindPlanIDs = NULL;
@ -164,6 +163,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
glob->resultRelations = NIL;
glob->relationOids = NIL;
glob->invalItems = NIL;
glob->nParamExec = 0;
glob->lastPHId = 0;
glob->lastRowMarkId = 0;
glob->transientPlan = false;
@ -243,7 +243,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams)
result->rowMarks = glob->finalrowmarks;
result->relationOids = glob->relationOids;
result->invalItems = glob->invalItems;
result->nParamExec = list_length(glob->paramlist);
result->nParamExec = glob->nParamExec;
return result;
}
@ -295,6 +295,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
root->glob = glob;
root->query_level = parent_root ? parent_root->query_level + 1 : 1;
root->parent_root = parent_root;
root->plan_params = NIL;
root->planner_cxt = CurrentMemoryContext;
root->init_plans = NIL;
root->cte_plan_ids = NIL;
@ -586,7 +587,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
* and attach the initPlans to the top plan node.
*/
if (list_length(glob->subplans) != num_old_subplans ||
root->glob->paramlist != NIL)
root->glob->nParamExec > 0)
SS_finalize_plan(root, plan, true);
/* Return internal info if caller wants it */

View File

@ -54,6 +54,7 @@ typedef struct finalize_primnode_context
static Node *build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
List *plan_params,
SubLinkType subLinkType, Node *testexpr,
bool adjust_testexpr, bool unknownEqFalse);
static List *generate_subquery_params(PlannerInfo *root, List *tlist,
@ -82,35 +83,42 @@ static bool finalize_primnode(Node *node, finalize_primnode_context *context);
/*
* Select a PARAM_EXEC number to identify the given Var.
* If the Var already has a param slot, return that one.
* Select a PARAM_EXEC number to identify the given Var as a parameter for
* the current subquery, or for a nestloop's inner scan.
* If the Var already has a param in the current context, return that one.
*/
static int
assign_param_for_var(PlannerInfo *root, Var *var)
{
ListCell *ppl;
PlannerParamItem *pitem;
Index abslevel;
int i;
Index levelsup;
abslevel = root->query_level - var->varlevelsup;
/* Find the query level the Var belongs to */
for (levelsup = var->varlevelsup; levelsup > 0; levelsup--)
root = root->parent_root;
/* If there's already a paramlist entry for this same Var, just use it */
i = 0;
foreach(ppl, root->glob->paramlist)
/* If there's already a matching PlannerParamItem there, just use it */
foreach(ppl, root->plan_params)
{
pitem = (PlannerParamItem *) lfirst(ppl);
if (pitem->abslevel == abslevel && IsA(pitem->item, Var))
if (IsA(pitem->item, Var))
{
Var *pvar = (Var *) pitem->item;
/*
* This comparison must match _equalVar(), except for ignoring
* varlevelsup. Note that _equalVar() ignores the location.
*/
if (pvar->varno == var->varno &&
pvar->varattno == var->varattno &&
pvar->vartype == var->vartype &&
pvar->vartypmod == var->vartypmod)
return i;
pvar->vartypmod == var->vartypmod &&
pvar->varcollid == var->varcollid &&
pvar->varnoold == var->varnoold &&
pvar->varoattno == var->varoattno)
return pitem->paramId;
}
i++;
}
/* Nope, so make a new one */
@ -119,12 +127,11 @@ assign_param_for_var(PlannerInfo *root, Var *var)
pitem = makeNode(PlannerParamItem);
pitem->item = (Node *) var;
pitem->abslevel = abslevel;
pitem->paramId = root->glob->nParamExec++;
root->glob->paramlist = lappend(root->glob->paramlist, pitem);
root->plan_params = lappend(root->plan_params, pitem);
/* i is already the correct list index for the new item */
return i;
return pitem->paramId;
}
/*
@ -139,16 +146,7 @@ replace_outer_var(PlannerInfo *root, Var *var)
Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level);
/*
* Find the Var in root->glob->paramlist, or add it if not present.
*
* NOTE: in sufficiently complex querytrees, it is possible for the same
* varno/abslevel to refer to different RTEs in different parts of the
* parsetree, so that different fields might end up sharing the same Param
* number. As long as we check the vartype/typmod as well, I believe that
* this sort of aliasing will cause no trouble. The correct field should
* get stored into the Param slot at execution in each part of the tree.
*/
/* Find the Var in the appropriate plan_params, or add it if not present */
i = assign_param_for_var(root, var);
retval = makeNode(Param);
@ -157,7 +155,7 @@ replace_outer_var(PlannerInfo *root, Var *var)
retval->paramtype = var->vartype;
retval->paramtypmod = var->vartypmod;
retval->paramcollid = var->varcollid;
retval->location = -1;
retval->location = var->location;
return retval;
}
@ -166,8 +164,7 @@ replace_outer_var(PlannerInfo *root, Var *var)
* Generate a Param node to replace the given Var, which will be supplied
* from an upper NestLoop join node.
*
* Because we allow nestloop and subquery Params to alias each other,
* this is effectively the same as replace_outer_var, except that we expect
* This is effectively the same as replace_outer_var, except that we expect
* the Var to be local to the current query level.
*/
Param *
@ -186,14 +183,15 @@ assign_nestloop_param_var(PlannerInfo *root, Var *var)
retval->paramtype = var->vartype;
retval->paramtypmod = var->vartypmod;
retval->paramcollid = var->varcollid;
retval->location = -1;
retval->location = var->location;
return retval;
}
/*
* Select a PARAM_EXEC number to identify the given PlaceHolderVar.
* If the PlaceHolderVar already has a param slot, return that one.
* Select a PARAM_EXEC number to identify the given PlaceHolderVar as a
* parameter for the current subquery, or for a nestloop's inner scan.
* If the PHV already has a param in the current context, return that one.
*
* This is just like assign_param_for_var, except for PlaceHolderVars.
*/
@ -202,25 +200,24 @@ assign_param_for_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
{
ListCell *ppl;
PlannerParamItem *pitem;
Index abslevel;
int i;
Index levelsup;
abslevel = root->query_level - phv->phlevelsup;
/* Find the query level the PHV belongs to */
for (levelsup = phv->phlevelsup; levelsup > 0; levelsup--)
root = root->parent_root;
/* If there's already a paramlist entry for this same PHV, just use it */
i = 0;
foreach(ppl, root->glob->paramlist)
/* If there's already a matching PlannerParamItem there, just use it */
foreach(ppl, root->plan_params)
{
pitem = (PlannerParamItem *) lfirst(ppl);
if (pitem->abslevel == abslevel && IsA(pitem->item, PlaceHolderVar))
if (IsA(pitem->item, PlaceHolderVar))
{
PlaceHolderVar *pphv = (PlaceHolderVar *) pitem->item;
/* We assume comparing the PHIDs is sufficient */
if (pphv->phid == phv->phid)
return i;
return pitem->paramId;
}
i++;
}
/* Nope, so make a new one */
@ -233,12 +230,11 @@ assign_param_for_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
pitem = makeNode(PlannerParamItem);
pitem->item = (Node *) phv;
pitem->abslevel = abslevel;
pitem->paramId = root->glob->nParamExec++;
root->glob->paramlist = lappend(root->glob->paramlist, pitem);
root->plan_params = lappend(root->plan_params, pitem);
/* i is already the correct list index for the new item */
return i;
return pitem->paramId;
}
/*
@ -255,10 +251,7 @@ replace_outer_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv)
Assert(phv->phlevelsup > 0 && phv->phlevelsup < root->query_level);
/*
* Find the PlaceHolderVar in root->glob->paramlist, or add it if not
* present.
*/
/* Find the PHV in the appropriate plan_params, or add it if not present */
i = assign_param_for_placeholdervar(root, phv);
retval = makeNode(Param);
@ -308,11 +301,13 @@ replace_outer_agg(PlannerInfo *root, Aggref *agg)
{
Param *retval;
PlannerParamItem *pitem;
Index abslevel;
int i;
Index levelsup;
Assert(agg->agglevelsup > 0 && agg->agglevelsup < root->query_level);
abslevel = root->query_level - agg->agglevelsup;
/* Find the query level the Aggref belongs to */
for (levelsup = agg->agglevelsup; levelsup > 0; levelsup--)
root = root->parent_root;
/*
* It does not seem worthwhile to try to match duplicate outer aggs. Just
@ -324,18 +319,17 @@ replace_outer_agg(PlannerInfo *root, Aggref *agg)
pitem = makeNode(PlannerParamItem);
pitem->item = (Node *) agg;
pitem->abslevel = abslevel;
pitem->paramId = root->glob->nParamExec++;
root->glob->paramlist = lappend(root->glob->paramlist, pitem);
i = list_length(root->glob->paramlist) - 1;
root->plan_params = lappend(root->plan_params, pitem);
retval = makeNode(Param);
retval->paramkind = PARAM_EXEC;
retval->paramid = i;
retval->paramid = pitem->paramId;
retval->paramtype = agg->aggtype;
retval->paramtypmod = -1;
retval->paramcollid = agg->aggcollid;
retval->location = -1;
retval->location = agg->location;
return retval;
}
@ -343,29 +337,24 @@ replace_outer_agg(PlannerInfo *root, Aggref *agg)
/*
* Generate a new Param node that will not conflict with any other.
*
* This is used to allocate PARAM_EXEC slots for subplan outputs.
* This is used to create Params representing subplan outputs.
* We don't need to build a PlannerParamItem for such a Param, but we do
* need to record the PARAM_EXEC slot number as being allocated.
*/
static Param *
generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod,
Oid paramcollation)
{
Param *retval;
PlannerParamItem *pitem;
retval = makeNode(Param);
retval->paramkind = PARAM_EXEC;
retval->paramid = list_length(root->glob->paramlist);
retval->paramid = root->glob->nParamExec++;
retval->paramtype = paramtype;
retval->paramtypmod = paramtypmod;
retval->paramcollid = paramcollation;
retval->location = -1;
pitem = makeNode(PlannerParamItem);
pitem->item = (Node *) retval;
pitem->abslevel = root->query_level;
root->glob->paramlist = lappend(root->glob->paramlist, pitem);
return retval;
}
@ -374,17 +363,13 @@ generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod,
* is not actually used to carry a value at runtime). Such parameters are
* used for special runtime signaling purposes, such as connecting a
* recursive union node to its worktable scan node or forcing plan
* re-evaluation within the EvalPlanQual mechanism.
* re-evaluation within the EvalPlanQual mechanism. No actual Param node
* exists with this ID, however.
*/
int
SS_assign_special_param(PlannerInfo *root)
{
Param *param;
/* We generate a Param of datatype INTERNAL */
param = generate_new_param(root, INTERNALOID, -1, InvalidOid);
/* ... but the caller only cares about its ID */
return param->paramid;
return root->glob->nParamExec++;
}
/*
@ -445,6 +430,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, SubLinkType subLinkType,
double tuple_fraction;
Plan *plan;
PlannerInfo *subroot;
List *plan_params;
Node *result;
/*
@ -488,6 +474,9 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, SubLinkType subLinkType,
else
tuple_fraction = 0.0; /* default behavior */
/* plan_params should not be in use in current query level */
Assert(root->plan_params == NIL);
/*
* Generate the plan for the subquery.
*/
@ -496,8 +485,12 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, SubLinkType subLinkType,
false, tuple_fraction,
&subroot);
/* Isolate the params needed by this specific subplan */
plan_params = root->plan_params;
root->plan_params = NIL;
/* And convert to SubPlan or InitPlan format. */
result = build_subplan(root, plan, subroot,
result = build_subplan(root, plan, subroot, plan_params,
subLinkType, testexpr, true, isTopQual);
/*
@ -530,6 +523,10 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, SubLinkType subLinkType,
false, 0.0,
&subroot);
/* Isolate the params needed by this specific subplan */
plan_params = root->plan_params;
root->plan_params = NIL;
/* Now we can check if it'll fit in work_mem */
if (subplan_is_hashable(plan))
{
@ -538,6 +535,7 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, SubLinkType subLinkType,
/* OK, convert to SubPlan format. */
hashplan = (SubPlan *) build_subplan(root, plan, subroot,
plan_params,
ANY_SUBLINK, newtestexpr,
false, true);
/* Check we got what we expected */
@ -566,14 +564,14 @@ make_subplan(PlannerInfo *root, Query *orig_subquery, SubLinkType subLinkType,
*/
static Node *
build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
List *plan_params,
SubLinkType subLinkType, Node *testexpr,
bool adjust_testexpr, bool unknownEqFalse)
{
Node *result;
SubPlan *splan;
bool isInitPlan;
Bitmapset *tmpset;
int paramid;
ListCell *lc;
/*
* Initialize the SubPlan node. Note plan_id, plan_name, and cost fields
@ -595,36 +593,26 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot,
* Make parParam and args lists of param IDs and expressions that current
* query level will pass to this child plan.
*/
tmpset = bms_copy(plan->extParam);
while ((paramid = bms_first_member(tmpset)) >= 0)
foreach(lc, plan_params)
{
PlannerParamItem *pitem = list_nth(root->glob->paramlist, paramid);
PlannerParamItem *pitem = (PlannerParamItem *) lfirst(lc);
Node *arg = pitem->item;
if (pitem->abslevel == root->query_level)
{
Node *arg;
/*
* The Var, PlaceHolderVar, or Aggref has already been adjusted to
* have the correct varlevelsup, phlevelsup, or agglevelsup.
*
* If it's a PlaceHolderVar or Aggref, its arguments might contain
* SubLinks, which have not yet been processed (see the comments for
* SS_replace_correlation_vars). Do that now.
*/
if (IsA(arg, PlaceHolderVar) ||
IsA(arg, Aggref))
arg = SS_process_sublinks(root, arg, false);
/*
* The Var, PlaceHolderVar, or Aggref has already been adjusted to
* have the correct varlevelsup, phlevelsup, or agglevelsup. We
* probably don't even need to copy it again, but be safe.
*/
arg = copyObject(pitem->item);
/*
* If it's a PlaceHolderVar or Aggref, its arguments might contain
* SubLinks, which have not yet been processed (see the comments
* for SS_replace_correlation_vars). Do that now.
*/
if (IsA(arg, PlaceHolderVar) ||
IsA(arg, Aggref))
arg = SS_process_sublinks(root, arg, false);
splan->parParam = lappend_int(splan->parParam, paramid);
splan->args = lappend(splan->args, arg);
}
splan->parParam = lappend_int(splan->parParam, pitem->paramId);
splan->args = lappend(splan->args, arg);
}
bms_free(tmpset);
/*
* Un-correlated or undirect correlated plans of EXISTS, EXPR, ARRAY, or
@ -1045,9 +1033,7 @@ SS_process_ctes(PlannerInfo *root)
Plan *plan;
PlannerInfo *subroot;
SubPlan *splan;
Bitmapset *tmpset;
int paramid;
Param *prm;
/*
* Ignore SELECT CTEs that are not actually referenced anywhere.
@ -1065,6 +1051,9 @@ SS_process_ctes(PlannerInfo *root)
*/
subquery = (Query *) copyObject(cte->ctequery);
/* plan_params should not be in use in current query level */
Assert(root->plan_params == NIL);
/*
* Generate the plan for the CTE query. Always plan for full
* retrieval --- we don't have enough info to predict otherwise.
@ -1074,6 +1063,14 @@ SS_process_ctes(PlannerInfo *root)
cte->cterecursive, 0.0,
&subroot);
/*
* Since the current query level doesn't yet contain any RTEs, it
* should not be possible for the CTE to have requested parameters of
* this level.
*/
if (root->plan_params)
elog(ERROR, "unexpected outer reference in CTE query");
/*
* Make a SubPlan node for it. This is just enough unlike
* build_subplan that we can't share code.
@ -1093,35 +1090,22 @@ SS_process_ctes(PlannerInfo *root)
splan->args = NIL;
/*
* Make parParam and args lists of param IDs and expressions that
* current query level will pass to this child plan. Even though this
* is an initplan, there could be side-references to earlier
* initplan's outputs, specifically their CTE output parameters.
* The node can't have any inputs (since it's an initplan), so the
* parParam and args lists remain empty. (It could contain references
* to earlier CTEs' output param IDs, but CTE outputs are not
* propagated via the args list.)
*/
tmpset = bms_copy(plan->extParam);
while ((paramid = bms_first_member(tmpset)) >= 0)
{
PlannerParamItem *pitem = list_nth(root->glob->paramlist, paramid);
if (pitem->abslevel == root->query_level)
{
prm = (Param *) pitem->item;
if (!IsA(prm, Param) ||
prm->paramtype != INTERNALOID)
elog(ERROR, "bogus local parameter passed to WITH query");
splan->parParam = lappend_int(splan->parParam, paramid);
splan->args = lappend(splan->args, copyObject(prm));
}
}
bms_free(tmpset);
/*
* Assign a param to represent the query output. We only really care
* about reserving a parameter ID number.
* Assign a param ID to represent the CTE's output. No ordinary
* "evaluation" of this param slot ever happens, but we use the param
* ID for setParam/chgParam signaling just as if the CTE plan were
* returning a simple scalar output. (Also, the executor abuses the
* ParamExecData slot for this param ID for communication among
* multiple CteScan nodes that might be scanning this CTE.)
*/
prm = generate_new_param(root, INTERNALOID, -1, InvalidOid);
splan->setParam = list_make1_int(prm->paramid);
paramid = SS_assign_special_param(root);
splan->setParam = list_make1_int(paramid);
/*
* Add the subplan and its PlannerInfo to the global lists.
@ -1932,7 +1916,7 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan, bool attach_initplans)
*initExtParam,
*initSetParam;
Cost initplan_cost;
int paramid;
PlannerInfo *proot;
ListCell *l;
/*
@ -1964,31 +1948,36 @@ SS_finalize_plan(PlannerInfo *root, Plan *plan, bool attach_initplans)
/*
* 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 initPlans. (We do not include output
* 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.)
*
* Note: this is a bit overly generous since some parameters of upper
* query levels might belong to query subtrees that don't include this
* query, or might be nestloop params that won't be passed down at all.
* However, valid_params is only a debugging crosscheck, so it doesn't
* seem worth expending lots of cycles to try to be exact.
*/
valid_params = bms_copy(initSetParam);
paramid = 0;
foreach(l, root->glob->paramlist)
for (proot = root->parent_root; proot != NULL; proot = proot->parent_root)
{
PlannerParamItem *pitem = (PlannerParamItem *) lfirst(l);
if (pitem->abslevel < root->query_level)
/* Include ordinary Var/PHV/Aggref params */
foreach(l, proot->plan_params)
{
/* valid outer-level parameter */
valid_params = bms_add_member(valid_params, paramid);
}
PlannerParamItem *pitem = (PlannerParamItem *) lfirst(l);
paramid++;
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);
}
/*

View File

@ -799,6 +799,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
subroot->glob = root->glob;
subroot->query_level = root->query_level;
subroot->parent_root = root->parent_root;
subroot->plan_params = NIL;
subroot->planner_cxt = CurrentMemoryContext;
subroot->init_plans = NIL;
subroot->cte_plan_ids = NIL;

View File

@ -238,6 +238,9 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
*/
rel = build_simple_rel(root, rtr->rtindex, RELOPT_BASEREL);
/* plan_params should not be in use in current query level */
Assert(root->plan_params == NIL);
/*
* Generate plan for primitive subquery
*/
@ -250,6 +253,13 @@ recurse_set_operations(Node *setOp, PlannerInfo *root,
rel->subplan = subplan;
rel->subroot = subroot;
/*
* It should not be possible for the primitive query to contain any
* cross-references to other primitive queries in the setop tree.
*/
if (root->plan_params)
elog(ERROR, "unexpected outer reference in set operation subquery");
/*
* Estimate number of groups if caller wants it. If the subquery used
* grouping or aggregation, its output is probably mostly unique

View File

@ -119,6 +119,7 @@ build_simple_rel(PlannerInfo *root, int relid, RelOptKind reloptkind)
rel->allvisfrac = 0;
rel->subplan = NULL;
rel->subroot = NULL;
rel->subplan_params = NIL;
rel->fdwroutine = NULL;
rel->fdw_private = NULL;
rel->baserestrictinfo = NIL;
@ -379,6 +380,7 @@ build_join_rel(PlannerInfo *root,
joinrel->allvisfrac = 0;
joinrel->subplan = NULL;
joinrel->subroot = NULL;
joinrel->subplan_params = NIL;
joinrel->fdwroutine = NULL;
joinrel->fdw_private = NULL;
joinrel->baserestrictinfo = NIL;