#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"
}
/*
- * 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 */
{
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)
foreach(l2, initsubplan->setParam)
{
- valid_params = bms_add_member(valid_params, lfirst_int(l2));
+ outer_params = bms_add_member(outer_params, lfirst_int(l2));
}
}
/* Include worktable ID, if a recursive query is being planned */
if (proot->wt_param_id >= 0)
- valid_params = bms_add_member(valid_params, proot->wt_param_id);
+ outer_params = bms_add_member(outer_params, proot->wt_param_id);
}
+ root->outer_params = outer_params;
+}
- /*
- * Now recurse through plan tree.
- */
- (void) finalize_plan(root, plan, valid_params, NULL);
-
- bms_free(valid_params);
+/*
+ * SS_attach_initplans - attach initplans to topmost plan node
+ *
+ * Attach any initplans created in the current query level to the topmost plan
+ * node for the query level, and increment that node's cost to account for
+ * them. (The initPlans could actually go in any node at or above where
+ * they're referenced, but there seems no reason to put them any lower than
+ * the topmost node for the query level.)
+ */
+void
+SS_attach_initplans(PlannerInfo *root, Plan *plan)
+{
+ ListCell *lc;
- /*
- * Finally, attach any initPlans to the topmost plan node, and add their
- * extParams to the topmost node's, too. However, any setParams of the
- * initPlans should not be present in the topmost node's extParams, only
- * in its allParams. (As of PG 8.1, it's possible that some initPlans
- * have extParams that are setParams of other initPlans, so we have to
- * take care of this situation explicitly.)
- *
- * We also add the eval cost of each initPlan to the startup cost of the
- * top node. This is a conservative overestimate, since in fact each
- * initPlan might be executed later than plan startup, or even not at all.
- */
- if (attach_initplans)
+ plan->initPlan = root->init_plans;
+ foreach(lc, plan->initPlan)
{
- plan->initPlan = root->init_plans;
- root->init_plans = NIL; /* make sure they're not attached twice */
-
- /* allParam must include all these params */
- plan->allParam = bms_add_members(plan->allParam, initExtParam);
- plan->allParam = bms_add_members(plan->allParam, initSetParam);
- /* extParam must include any child extParam */
- plan->extParam = bms_add_members(plan->extParam, initExtParam);
- /* but extParam shouldn't include any setParams */
- plan->extParam = bms_del_members(plan->extParam, initSetParam);
- /* ensure extParam is exactly NULL if it's empty */
- if (bms_is_empty(plan->extParam))
- plan->extParam = NULL;
+ SubPlan *initsubplan = (SubPlan *) lfirst(lc);
+ Cost initplan_cost;
+
+ /*
+ * Assume each initPlan gets run once during top plan startup. This
+ * is a conservative overestimate, since in fact an initPlan might be
+ * executed later than plan startup, or even not at all.
+ */
+ initplan_cost = initsubplan->startup_cost + initsubplan->per_call_cost;
plan->startup_cost += initplan_cost;
plan->total_cost += initplan_cost;
}
}
+/*
+ * SS_finalize_plan - do final parameter processing for a completed Plan.
+ *
+ * This recursively computes the extParam and allParam sets for every Plan
+ * node in the given plan tree. (Oh, and RangeTblFunction.funcparams too.)
+ *
+ * We assume that SS_finalize_plan has already been run on any initplans or
+ * subplans the plan tree could reference.
+ */
+void
+SS_finalize_plan(PlannerInfo *root, Plan *plan)
+{
+ /* No setup needed, just recurse through plan tree. */
+ (void) finalize_plan(root, plan, root->outer_params, NULL);
+}
+
/*
* Recursive processing of all nodes in the plan tree
*
- * valid_params is the set of param IDs considered valid to reference in
- * this plan node or its children.
+ * valid_params is the set of param IDs supplied by outer plan levels
+ * that are valid to reference in this plan node or its children.
+ *
* scan_params is a set of param IDs to force scan plan nodes to reference.
* This is for EvalPlanQual support, and is always NULL at the top of the
* recursion.
*
* The return value is the computed allParam set for the given Plan node.
* This is just an internal notational convenience.
+ *
+ * Do not scribble on caller's values of valid_params or scan_params!
*/
static Bitmapset *
finalize_plan(PlannerInfo *root, Plan *plan, Bitmapset *valid_params,
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;
locally_added_param = -1; /* there isn't one */
nestloop_params = NULL; /* there aren't any */
+ /*
+ * Examine any initPlans to determine the set of external params they
+ * reference and the set of output params they supply. (We assume
+ * SS_finalize_plan was run on them already.)
+ */
+ initExtParam = initSetParam = NULL;
+ foreach(l, plan->initPlan)
+ {
+ SubPlan *initsubplan = (SubPlan *) lfirst(l);
+ Plan *initplan = planner_subplan_get_plan(root, initsubplan);
+ ListCell *l2;
+
+ initExtParam = bms_add_members(initExtParam, initplan->extParam);
+ foreach(l2, initsubplan->setParam)
+ {
+ initSetParam = bms_add_member(initSetParam, lfirst_int(l2));
+ }
+ }
+
+ /* Any setParams are validly referenceable in this node and children */
+ if (initSetParam)
+ valid_params = bms_union(valid_params, initSetParam);
+
/*
* When we call finalize_primnode, context.paramids sets are automatically
* merged together. But when recursing to self, we have to do it the hard
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:
* 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;
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;
}
/*
* 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)
*
* 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
* 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.