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,
/*
- * 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 */
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;
}
/*
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);
retval->paramtype = var->vartype;
retval->paramtypmod = var->vartypmod;
retval->paramcollid = var->varcollid;
- retval->location = -1;
+ retval->location = var->location;
return retval;
}
* 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 *
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.
*/
{
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 */
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;
}
/*
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);
{
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
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;
}
/*
* 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;
}
* 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++;
}
/*
double tuple_fraction;
Plan *plan;
PlannerInfo *subroot;
+ List *plan_params;
Node *result;
/*
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.
*/
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);
/*
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))
{
/* 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 */
*/
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
* 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);
-
- 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. We
- * probably don't even need to copy it again, but be safe.
- */
- arg = copyObject(pitem->item);
+ PlannerParamItem *pitem = (PlannerParamItem *) lfirst(lc);
+ Node *arg = 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);
+ /*
+ * 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);
- 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
Plan *plan;
PlannerInfo *subroot;
SubPlan *splan;
- Bitmapset *tmpset;
int paramid;
- Param *prm;
/*
* Ignore SELECT CTEs that are not actually referenced anywhere.
*/
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.
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.
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.
*initExtParam,
*initSetParam;
Cost initplan_cost;
- int paramid;
+ PlannerInfo *proot;
ListCell *l;
/*
/*
* 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);
+
+ 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;
- paramid++;
+ 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);
}
/*