diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 066685c3c7..97d0c28132 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -19,7 +19,6 @@ #include #include -#include "access/stratnum.h" #include "access/sysattr.h" #include "catalog/pg_class.h" #include "foreign/fdwapi.h" @@ -29,11 +28,11 @@ #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/paramassign.h" #include "optimizer/paths.h" #include "optimizer/placeholder.h" #include "optimizer/plancat.h" #include "optimizer/planmain.h" -#include "optimizer/planner.h" #include "optimizer/predtest.h" #include "optimizer/restrictinfo.h" #include "optimizer/subselect.h" @@ -151,8 +150,6 @@ static MergeJoin *create_mergejoin_plan(PlannerInfo *root, MergePath *best_path) static HashJoin *create_hashjoin_plan(PlannerInfo *root, HashPath *best_path); static Node *replace_nestloop_params(PlannerInfo *root, Node *expr); static Node *replace_nestloop_params_mutator(Node *node, PlannerInfo *root); -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); @@ -310,7 +307,7 @@ create_plan(PlannerInfo *root, Path *best_path) /* plan_params should not be in use in current query level */ Assert(root->plan_params == NIL); - /* Initialize this module's private workspace in PlannerInfo */ + /* Initialize this module's workspace in PlannerInfo */ root->curOuterRels = NULL; root->curOuterParams = NIL; @@ -1554,7 +1551,7 @@ create_gather_plan(PlannerInfo *root, GatherPath *best_path) gather_plan = make_gather(tlist, NIL, best_path->num_workers, - SS_assign_special_param(root), + assign_special_exec_param(root), best_path->single_copy, subplan); @@ -1590,7 +1587,7 @@ create_gather_merge_plan(PlannerInfo *root, GatherMergePath *best_path) copy_generic_path_info(&gm_plan->plan, &best_path->path); /* Assign the rescan Param. */ - gm_plan->rescan_param = SS_assign_special_param(root); + gm_plan->rescan_param = assign_special_exec_param(root); /* Gather Merge is pointless with no pathkeys; use Gather instead. */ Assert(pathkeys != NIL); @@ -3774,9 +3771,6 @@ create_nestloop_plan(PlannerInfo *root, Relids outerrelids; List *nestParams; Relids saveOuterRels = root->curOuterRels; - ListCell *cell; - ListCell *prev; - ListCell *next; /* NestLoop can project, so no need to be picky about child tlists */ outer_plan = create_plan_recurse(root, best_path->outerjoinpath, 0); @@ -3820,38 +3814,10 @@ create_nestloop_plan(PlannerInfo *root, /* * Identify any nestloop parameters that should be supplied by this join - * node, and move them from root->curOuterParams to the nestParams list. + * node, and remove them from root->curOuterParams. */ outerrelids = best_path->outerjoinpath->parent->relids; - nestParams = NIL; - prev = NULL; - for (cell = list_head(root->curOuterParams); cell; cell = next) - { - NestLoopParam *nlp = (NestLoopParam *) lfirst(cell); - - next = lnext(cell); - if (IsA(nlp->paramval, Var) && - bms_is_member(nlp->paramval->varno, outerrelids)) - { - root->curOuterParams = list_delete_cell(root->curOuterParams, - cell, prev); - nestParams = lappend(nestParams, nlp); - } - else if (IsA(nlp->paramval, PlaceHolderVar) && - bms_overlap(((PlaceHolderVar *) nlp->paramval)->phrels, - outerrelids) && - bms_is_subset(find_placeholder_info(root, - (PlaceHolderVar *) nlp->paramval, - false)->ph_eval_at, - outerrelids)) - { - root->curOuterParams = list_delete_cell(root->curOuterParams, - cell, prev); - nestParams = lappend(nestParams, nlp); - } - else - prev = cell; - } + nestParams = identify_current_nestloop_params(root, outerrelids); join_plan = make_nestloop(tlist, joinclauses, @@ -4351,42 +4317,18 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root) if (IsA(node, Var)) { Var *var = (Var *) node; - Param *param; - NestLoopParam *nlp; - ListCell *lc; /* Upper-level Vars should be long gone at this point */ Assert(var->varlevelsup == 0); /* If not to be replaced, we can just return the Var unmodified */ if (!bms_is_member(var->varno, root->curOuterRels)) return node; - /* Create a Param representing the Var */ - param = assign_nestloop_param_var(root, var); - /* Is this param already listed in root->curOuterParams? */ - foreach(lc, root->curOuterParams) - { - nlp = (NestLoopParam *) lfirst(lc); - if (nlp->paramno == param->paramid) - { - Assert(equal(var, nlp->paramval)); - /* Present, so we can just return the Param */ - return (Node *) param; - } - } - /* No, so add it */ - nlp = makeNode(NestLoopParam); - nlp->paramno = param->paramid; - nlp->paramval = var; - root->curOuterParams = lappend(root->curOuterParams, nlp); - /* And return the replacement Param */ - return (Node *) param; + /* Replace the Var with a nestloop Param */ + return (Node *) replace_nestloop_param_var(root, var); } if (IsA(node, PlaceHolderVar)) { PlaceHolderVar *phv = (PlaceHolderVar *) node; - Param *param; - NestLoopParam *nlp; - ListCell *lc; /* Upper-level PlaceHolderVars should be long gone at this point */ Assert(phv->phlevelsup == 0); @@ -4423,118 +4365,14 @@ replace_nestloop_params_mutator(Node *node, PlannerInfo *root) root); return (Node *) newphv; } - /* Create a Param representing the PlaceHolderVar */ - param = assign_nestloop_param_placeholdervar(root, phv); - /* Is this param already listed in root->curOuterParams? */ - foreach(lc, root->curOuterParams) - { - nlp = (NestLoopParam *) lfirst(lc); - if (nlp->paramno == param->paramid) - { - Assert(equal(phv, nlp->paramval)); - /* Present, so we can just return the Param */ - return (Node *) param; - } - } - /* No, so add it */ - nlp = makeNode(NestLoopParam); - nlp->paramno = param->paramid; - nlp->paramval = (Var *) phv; - root->curOuterParams = lappend(root->curOuterParams, nlp); - /* And return the replacement Param */ - return (Node *) param; + /* Replace the PlaceHolderVar with a nestloop Param */ + return (Node *) replace_nestloop_param_placeholdervar(root, phv); } return expression_tree_mutator(node, replace_nestloop_params_mutator, (void *) root); } -/* - * 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 -process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params) -{ - ListCell *ppl; - - foreach(ppl, subplan_params) - { - PlannerParamItem *pitem = (PlannerParamItem *) lfirst(ppl); - - if (IsA(pitem->item, Var)) - { - Var *var = (Var *) pitem->item; - NestLoopParam *nlp; - ListCell *lc; - - /* If not from a nestloop outer rel, complain */ - if (!bms_is_member(var->varno, root->curOuterRels)) - 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 == pitem->paramId) - { - Assert(equal(var, nlp->paramval)); - /* Present, so nothing to do */ - break; - } - } - if (lc == NULL) - { - /* No, so add it */ - nlp = makeNode(NestLoopParam); - nlp->paramno = pitem->paramId; - nlp->paramval = copyObject(var); - root->curOuterParams = lappend(root->curOuterParams, nlp); - } - } - else if (IsA(pitem->item, PlaceHolderVar)) - { - PlaceHolderVar *phv = (PlaceHolderVar *) pitem->item; - NestLoopParam *nlp; - ListCell *lc; - - /* If not from a nestloop outer rel, complain */ - if (!bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at, - root->curOuterRels)) - 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 == pitem->paramId) - { - Assert(equal(phv, nlp->paramval)); - /* Present, so nothing to do */ - break; - } - } - if (lc == NULL) - { - /* No, so add it */ - nlp = makeNode(NestLoopParam); - nlp->paramno = pitem->paramId; - nlp->paramval = (Var *) copyObject(phv); - root->curOuterParams = lappend(root->curOuterParams, nlp); - } - } - else - elog(ERROR, "unexpected type of subquery parameter"); - } -} - /* * fix_indexqual_references * Adjust indexqual clauses to the form the executor's indexqual diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 98acb58304..b849ae03b8 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -41,6 +41,7 @@ #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/inherit.h" +#include "optimizer/paramassign.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" #include "optimizer/plancat.h" @@ -635,7 +636,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, root->inhTargetKind = INHKIND_NONE; root->hasRecursion = hasRecursion; if (hasRecursion) - root->wt_param_id = SS_assign_special_param(root); + root->wt_param_id = assign_special_exec_param(root); else root->wt_param_id = -1; root->non_recursive_path = NULL; @@ -1616,7 +1617,7 @@ inheritance_planner(PlannerInfo *root) returningLists, rowMarks, NULL, - SS_assign_special_param(root))); + assign_special_exec_param(root))); } /*-------------------- @@ -2131,7 +2132,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, { path = (Path *) create_lockrows_path(root, final_rel, path, root->rowMarks, - SS_assign_special_param(root)); + assign_special_exec_param(root)); } /* @@ -2204,7 +2205,7 @@ grouping_planner(PlannerInfo *root, bool inheritance_update, returningLists, rowMarks, parse->onConflict, - SS_assign_special_param(root)); + assign_special_exec_param(root)); } /* And shove it into final_rel */ diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index cb1ea86afb..64272dd650 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -1,7 +1,10 @@ /*------------------------------------------------------------------------- * * subselect.c - * Planning routines for subselects and parameters. + * Planning routines for subselects. + * + * This module deals with SubLinks and CTEs, but not subquery RTEs (i.e., + * not sub-SELECT-in-FROM cases). * * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California @@ -22,6 +25,7 @@ #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" #include "optimizer/cost.h" +#include "optimizer/paramassign.h" #include "optimizer/pathnode.h" #include "optimizer/planmain.h" #include "optimizer/planner.h" @@ -86,355 +90,6 @@ static bool finalize_primnode(Node *node, finalize_primnode_context *context); static bool finalize_agg_primnode(Node *node, finalize_primnode_context *context); -/* - * 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 levelsup; - - /* Find the query level the Var belongs to */ - for (levelsup = var->varlevelsup; levelsup > 0; levelsup--) - root = root->parent_root; - - /* If there's already a matching PlannerParamItem there, just use it */ - foreach(ppl, root->plan_params) - { - pitem = (PlannerParamItem *) lfirst(ppl); - 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 && - pvar->varcollid == var->varcollid && - pvar->varnoold == var->varnoold && - pvar->varoattno == var->varoattno) - return pitem->paramId; - } - } - - /* Nope, so make a new one */ - var = copyObject(var); - var->varlevelsup = 0; - - pitem = makeNode(PlannerParamItem); - pitem->item = (Node *) var; - pitem->paramId = list_length(root->glob->paramExecTypes); - root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes, - var->vartype); - - root->plan_params = lappend(root->plan_params, pitem); - - return pitem->paramId; -} - -/* - * Generate a Param node to replace the given Var, - * which is expected to have varlevelsup > 0 (ie, it is not local). - */ -static Param * -replace_outer_var(PlannerInfo *root, Var *var) -{ - Param *retval; - int i; - - Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level); - - /* 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->paramkind = PARAM_EXEC; - retval->paramid = i; - retval->paramtype = var->vartype; - retval->paramtypmod = var->vartypmod; - retval->paramcollid = var->varcollid; - 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. - * - * This is effectively the same as replace_outer_var, except that we expect - * the Var to be local to the current query level. - */ -Param * -assign_nestloop_param_var(PlannerInfo *root, Var *var) -{ - Param *retval; - int i; - - Assert(var->varlevelsup == 0); - - i = assign_param_for_var(root, var); - - retval = makeNode(Param); - retval->paramkind = PARAM_EXEC; - retval->paramid = i; - retval->paramtype = var->vartype; - retval->paramtypmod = var->vartypmod; - retval->paramcollid = var->varcollid; - retval->location = var->location; - - return retval; -} - -/* - * 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. - */ -static int -assign_param_for_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv) -{ - ListCell *ppl; - PlannerParamItem *pitem; - Index levelsup; - - /* Find the query level the PHV belongs to */ - for (levelsup = phv->phlevelsup; levelsup > 0; levelsup--) - root = root->parent_root; - - /* If there's already a matching PlannerParamItem there, just use it */ - foreach(ppl, root->plan_params) - { - pitem = (PlannerParamItem *) lfirst(ppl); - if (IsA(pitem->item, PlaceHolderVar)) - { - PlaceHolderVar *pphv = (PlaceHolderVar *) pitem->item; - - /* We assume comparing the PHIDs is sufficient */ - if (pphv->phid == phv->phid) - return pitem->paramId; - } - } - - /* Nope, so make a new one */ - phv = copyObject(phv); - if (phv->phlevelsup != 0) - { - IncrementVarSublevelsUp((Node *) phv, -((int) phv->phlevelsup), 0); - Assert(phv->phlevelsup == 0); - } - - pitem = makeNode(PlannerParamItem); - pitem->item = (Node *) phv; - pitem->paramId = list_length(root->glob->paramExecTypes); - root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes, - exprType((Node *) phv->phexpr)); - - root->plan_params = lappend(root->plan_params, pitem); - - return pitem->paramId; -} - -/* - * Generate a Param node to replace the given PlaceHolderVar, - * which is expected to have phlevelsup > 0 (ie, it is not local). - * - * This is just like replace_outer_var, except for PlaceHolderVars. - */ -static Param * -replace_outer_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv) -{ - Param *retval; - int i; - - Assert(phv->phlevelsup > 0 && phv->phlevelsup < root->query_level); - - /* Find the PHV in the appropriate plan_params, or add it if not present */ - i = assign_param_for_placeholdervar(root, phv); - - retval = makeNode(Param); - retval->paramkind = PARAM_EXEC; - retval->paramid = i; - retval->paramtype = exprType((Node *) phv->phexpr); - retval->paramtypmod = exprTypmod((Node *) phv->phexpr); - retval->paramcollid = exprCollation((Node *) phv->phexpr); - retval->location = -1; - - return retval; -} - -/* - * Generate a Param node to replace the given PlaceHolderVar, which will be - * supplied from an upper NestLoop join node. - * - * This is just like assign_nestloop_param_var, except for PlaceHolderVars. - */ -Param * -assign_nestloop_param_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv) -{ - Param *retval; - int i; - - Assert(phv->phlevelsup == 0); - - i = assign_param_for_placeholdervar(root, phv); - - retval = makeNode(Param); - retval->paramkind = PARAM_EXEC; - retval->paramid = i; - retval->paramtype = exprType((Node *) phv->phexpr); - retval->paramtypmod = exprTypmod((Node *) phv->phexpr); - retval->paramcollid = exprCollation((Node *) phv->phexpr); - retval->location = -1; - - return retval; -} - -/* - * Generate a Param node to replace the given Aggref - * which is expected to have agglevelsup > 0 (ie, it is not local). - */ -static Param * -replace_outer_agg(PlannerInfo *root, Aggref *agg) -{ - Param *retval; - PlannerParamItem *pitem; - Index levelsup; - - Assert(agg->agglevelsup > 0 && agg->agglevelsup < root->query_level); - - /* 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 - * make a new slot every time. - */ - agg = copyObject(agg); - IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0); - Assert(agg->agglevelsup == 0); - - pitem = makeNode(PlannerParamItem); - pitem->item = (Node *) agg; - pitem->paramId = list_length(root->glob->paramExecTypes); - root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes, - agg->aggtype); - - root->plan_params = lappend(root->plan_params, pitem); - - retval = makeNode(Param); - retval->paramkind = PARAM_EXEC; - retval->paramid = pitem->paramId; - retval->paramtype = agg->aggtype; - retval->paramtypmod = -1; - retval->paramcollid = agg->aggcollid; - retval->location = agg->location; - - return retval; -} - -/* - * Generate a Param node to replace the given GroupingFunc expression which is - * expected to have agglevelsup > 0 (ie, it is not local). - */ -static Param * -replace_outer_grouping(PlannerInfo *root, GroupingFunc *grp) -{ - Param *retval; - PlannerParamItem *pitem; - Index levelsup; - Oid ptype; - - Assert(grp->agglevelsup > 0 && grp->agglevelsup < root->query_level); - - /* Find the query level the GroupingFunc belongs to */ - for (levelsup = grp->agglevelsup; levelsup > 0; levelsup--) - root = root->parent_root; - - /* - * It does not seem worthwhile to try to match duplicate outer aggs. Just - * make a new slot every time. - */ - grp = copyObject(grp); - IncrementVarSublevelsUp((Node *) grp, -((int) grp->agglevelsup), 0); - Assert(grp->agglevelsup == 0); - ptype = exprType((Node *) grp); - - pitem = makeNode(PlannerParamItem); - pitem->item = (Node *) grp; - pitem->paramId = list_length(root->glob->paramExecTypes); - root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes, - ptype); - - root->plan_params = lappend(root->plan_params, pitem); - - retval = makeNode(Param); - retval->paramkind = PARAM_EXEC; - retval->paramid = pitem->paramId; - retval->paramtype = ptype; - retval->paramtypmod = -1; - retval->paramcollid = InvalidOid; - retval->location = grp->location; - - return retval; -} - -/* - * Generate a new Param node that will not conflict with any other. - * - * 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 make sure we record the type in paramExecTypes (otherwise, - * there won't be a slot allocated for it). - */ -static Param * -generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod, - Oid paramcollation) -{ - Param *retval; - - retval = makeNode(Param); - retval->paramkind = PARAM_EXEC; - retval->paramid = list_length(root->glob->paramExecTypes); - root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes, - paramtype); - retval->paramtype = paramtype; - retval->paramtypmod = paramtypmod; - retval->paramcollid = paramcollation; - retval->location = -1; - - return retval; -} - -/* - * Assign a (nonnegative) PARAM_EXEC ID for a special parameter (one that - * 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. No actual Param node - * exists with this ID, however. - */ -int -SS_assign_special_param(PlannerInfo *root) -{ - int paramId = list_length(root->glob->paramExecTypes); - - root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes, - InvalidOid); - return paramId; -} - /* * Get the datatype/typmod/collation of the first column of the plan's output. * @@ -716,7 +371,7 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, Param *prm; Assert(testexpr == NULL); - prm = generate_new_param(root, BOOLOID, -1, InvalidOid); + prm = generate_new_exec_param(root, BOOLOID, -1, InvalidOid); splan->setParam = list_make1_int(prm->paramid); isInitPlan = true; result = (Node *) prm; @@ -728,10 +383,10 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, Assert(!te->resjunk); Assert(testexpr == NULL); - prm = generate_new_param(root, - exprType((Node *) te->expr), - exprTypmod((Node *) te->expr), - exprCollation((Node *) te->expr)); + prm = generate_new_exec_param(root, + exprType((Node *) te->expr), + exprTypmod((Node *) te->expr), + exprCollation((Node *) te->expr)); splan->setParam = list_make1_int(prm->paramid); isInitPlan = true; result = (Node *) prm; @@ -748,10 +403,10 @@ build_subplan(PlannerInfo *root, Plan *plan, PlannerInfo *subroot, if (!OidIsValid(arraytype)) elog(ERROR, "could not find array type for datatype %s", format_type_be(exprType((Node *) te->expr))); - prm = generate_new_param(root, - arraytype, - exprTypmod((Node *) te->expr), - exprCollation((Node *) te->expr)); + prm = generate_new_exec_param(root, + arraytype, + exprTypmod((Node *) te->expr), + exprCollation((Node *) te->expr)); splan->setParam = list_make1_int(prm->paramid); isInitPlan = true; result = (Node *) prm; @@ -930,10 +585,10 @@ generate_subquery_params(PlannerInfo *root, List *tlist, List **paramIds) if (tent->resjunk) continue; - param = generate_new_param(root, - exprType((Node *) tent->expr), - exprTypmod((Node *) tent->expr), - exprCollation((Node *) tent->expr)); + param = generate_new_exec_param(root, + exprType((Node *) tent->expr), + exprTypmod((Node *) tent->expr), + exprCollation((Node *) tent->expr)); result = lappend(result, param); ids = lappend_int(ids, param->paramid); } @@ -1257,7 +912,7 @@ SS_process_ctes(PlannerInfo *root) * ParamExecData slot for this param ID for communication among * multiple CteScan nodes that might be scanning this CTE.) */ - paramid = SS_assign_special_param(root); + paramid = assign_special_exec_param(root); splan->setParam = list_make1_int(paramid); /* @@ -1863,10 +1518,10 @@ convert_EXISTS_to_ANY(PlannerInfo *root, Query *subselect, Param *param; cc = lnext(cc); - param = generate_new_param(root, - exprType(rightarg), - exprTypmod(rightarg), - exprCollation(rightarg)); + param = generate_new_exec_param(root, + exprType(rightarg), + exprTypmod(rightarg), + exprCollation(rightarg)); tlist = lappend(tlist, makeTargetEntry((Expr *) rightarg, resno++, @@ -2917,7 +2572,7 @@ finalize_primnode(Node *node, finalize_primnode_context *context) * parameter change signaling since we always re-evaluate the subplan. * Note that this wouldn't work too well if there might be uses of the * same param IDs elsewhere in the plan, but that can't happen because - * generate_new_param never tries to merge params. + * generate_new_exec_param never tries to merge params. */ foreach(lc, subplan->paramIds) { @@ -2983,7 +2638,8 @@ SS_make_initplan_output_param(PlannerInfo *root, Oid resulttype, int32 resulttypmod, Oid resultcollation) { - return generate_new_param(root, resulttype, resulttypmod, resultcollation); + return generate_new_exec_param(root, resulttype, + resulttypmod, resultcollation); } /* diff --git a/src/backend/optimizer/util/Makefile b/src/backend/optimizer/util/Makefile index adffb01226..bb33e2cc7c 100644 --- a/src/backend/optimizer/util/Makefile +++ b/src/backend/optimizer/util/Makefile @@ -12,8 +12,8 @@ subdir = src/backend/optimizer/util top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -OBJS = appendinfo.o clauses.o inherit.o joininfo.o orclauses.o pathnode.o \ - placeholder.o plancat.o predtest.o relnode.o restrictinfo.o tlist.o \ - var.o +OBJS = appendinfo.o clauses.o inherit.o joininfo.o orclauses.o \ + paramassign.o pathnode.o placeholder.o plancat.o predtest.o \ + relnode.o restrictinfo.o tlist.o var.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/optimizer/util/paramassign.c b/src/backend/optimizer/util/paramassign.c new file mode 100644 index 0000000000..838587c2b8 --- /dev/null +++ b/src/backend/optimizer/util/paramassign.c @@ -0,0 +1,599 @@ +/*------------------------------------------------------------------------- + * + * paramassign.c + * Functions for assigning PARAM_EXEC slots during planning. + * + * This module is responsible for managing three planner data structures: + * + * root->glob->paramExecTypes: records actual assignments of PARAM_EXEC slots. + * The i'th list element holds the data type OID of the i'th parameter slot. + * (Elements can be InvalidOid if they represent slots that are needed for + * chgParam signaling, but will never hold a value at runtime.) This list is + * global to the whole plan since the executor has only one PARAM_EXEC array. + * Assignments are permanent for the plan: we never remove entries once added. + * + * root->plan_params: a list of PlannerParamItem nodes, recording Vars and + * PlaceHolderVars that the root's query level needs to supply to lower-level + * subqueries, along with the PARAM_EXEC number to use for each such value. + * Elements are added to this list while planning a subquery, and the list + * is reset to empty after completion of each subquery. + * + * root->curOuterParams: a list of NestLoopParam nodes, recording Vars and + * PlaceHolderVars that some outer level of nestloop needs to pass down to + * a lower-level plan node in its righthand side. Elements are added to this + * list as createplan.c creates lower Plan nodes that need such Params, and + * are removed when it creates a NestLoop Plan node that will supply those + * values. + * + * The latter two data structures are used to prevent creating multiple + * PARAM_EXEC slots (each requiring work to fill) when the same upper + * SubPlan or NestLoop supplies a value that is referenced in more than + * one place in its child plan nodes. However, when the same Var has to + * be supplied to different subplan trees by different SubPlan or NestLoop + * parent nodes, we don't recognize any commonality; a fresh plan_params or + * curOuterParams entry will be made (since the old one has been removed + * when we finished processing the earlier SubPlan or NestLoop) and a fresh + * PARAM_EXEC number will be assigned. At one time we tried to avoid + * allocating duplicate PARAM_EXEC numbers in such cases, but it's harder + * than it seems to avoid bugs due to overlapping Param lifetimes, so we + * don't risk that anymore. Minimizing the number of PARAM_EXEC slots + * doesn't really save much executor work anyway. + * + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * IDENTIFICATION + * src/backend/optimizer/util/paramassign.c + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "nodes/nodeFuncs.h" +#include "nodes/plannodes.h" +#include "optimizer/paramassign.h" +#include "optimizer/placeholder.h" +#include "rewrite/rewriteManip.h" + + +/* + * Select a PARAM_EXEC number to identify the given Var as a parameter for + * the current subquery. (It might already have one.) + * Record the need for the Var in the proper upper-level root->plan_params. + */ +static int +assign_param_for_var(PlannerInfo *root, Var *var) +{ + ListCell *ppl; + PlannerParamItem *pitem; + Index levelsup; + + /* Find the query level the Var belongs to */ + for (levelsup = var->varlevelsup; levelsup > 0; levelsup--) + root = root->parent_root; + + /* If there's already a matching PlannerParamItem there, just use it */ + foreach(ppl, root->plan_params) + { + pitem = (PlannerParamItem *) lfirst(ppl); + 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 && + pvar->varcollid == var->varcollid && + pvar->varnoold == var->varnoold && + pvar->varoattno == var->varoattno) + return pitem->paramId; + } + } + + /* Nope, so make a new one */ + var = copyObject(var); + var->varlevelsup = 0; + + pitem = makeNode(PlannerParamItem); + pitem->item = (Node *) var; + pitem->paramId = list_length(root->glob->paramExecTypes); + root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes, + var->vartype); + + root->plan_params = lappend(root->plan_params, pitem); + + return pitem->paramId; +} + +/* + * Generate a Param node to replace the given Var, + * which is expected to have varlevelsup > 0 (ie, it is not local). + * Record the need for the Var in the proper upper-level root->plan_params. + */ +Param * +replace_outer_var(PlannerInfo *root, Var *var) +{ + Param *retval; + int i; + + Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level); + + /* 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->paramkind = PARAM_EXEC; + retval->paramid = i; + retval->paramtype = var->vartype; + retval->paramtypmod = var->vartypmod; + retval->paramcollid = var->varcollid; + retval->location = var->location; + + return retval; +} + +/* + * Select a PARAM_EXEC number to identify the given PlaceHolderVar as a + * parameter for the current subquery. (It might already have one.) + * Record the need for the PHV in the proper upper-level root->plan_params. + * + * This is just like assign_param_for_var, except for PlaceHolderVars. + */ +static int +assign_param_for_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv) +{ + ListCell *ppl; + PlannerParamItem *pitem; + Index levelsup; + + /* Find the query level the PHV belongs to */ + for (levelsup = phv->phlevelsup; levelsup > 0; levelsup--) + root = root->parent_root; + + /* If there's already a matching PlannerParamItem there, just use it */ + foreach(ppl, root->plan_params) + { + pitem = (PlannerParamItem *) lfirst(ppl); + if (IsA(pitem->item, PlaceHolderVar)) + { + PlaceHolderVar *pphv = (PlaceHolderVar *) pitem->item; + + /* We assume comparing the PHIDs is sufficient */ + if (pphv->phid == phv->phid) + return pitem->paramId; + } + } + + /* Nope, so make a new one */ + phv = copyObject(phv); + IncrementVarSublevelsUp((Node *) phv, -((int) phv->phlevelsup), 0); + Assert(phv->phlevelsup == 0); + + pitem = makeNode(PlannerParamItem); + pitem->item = (Node *) phv; + pitem->paramId = list_length(root->glob->paramExecTypes); + root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes, + exprType((Node *) phv->phexpr)); + + root->plan_params = lappend(root->plan_params, pitem); + + return pitem->paramId; +} + +/* + * Generate a Param node to replace the given PlaceHolderVar, + * which is expected to have phlevelsup > 0 (ie, it is not local). + * Record the need for the PHV in the proper upper-level root->plan_params. + * + * This is just like replace_outer_var, except for PlaceHolderVars. + */ +Param * +replace_outer_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv) +{ + Param *retval; + int i; + + Assert(phv->phlevelsup > 0 && phv->phlevelsup < root->query_level); + + /* Find the PHV in the appropriate plan_params, or add it if not present */ + i = assign_param_for_placeholdervar(root, phv); + + retval = makeNode(Param); + retval->paramkind = PARAM_EXEC; + retval->paramid = i; + retval->paramtype = exprType((Node *) phv->phexpr); + retval->paramtypmod = exprTypmod((Node *) phv->phexpr); + retval->paramcollid = exprCollation((Node *) phv->phexpr); + retval->location = -1; + + return retval; +} + +/* + * Generate a Param node to replace the given Aggref + * which is expected to have agglevelsup > 0 (ie, it is not local). + * Record the need for the Aggref in the proper upper-level root->plan_params. + */ +Param * +replace_outer_agg(PlannerInfo *root, Aggref *agg) +{ + Param *retval; + PlannerParamItem *pitem; + Index levelsup; + + Assert(agg->agglevelsup > 0 && agg->agglevelsup < root->query_level); + + /* 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 de-duplicate references to outer + * aggs. Just make a new slot every time. + */ + agg = copyObject(agg); + IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0); + Assert(agg->agglevelsup == 0); + + pitem = makeNode(PlannerParamItem); + pitem->item = (Node *) agg; + pitem->paramId = list_length(root->glob->paramExecTypes); + root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes, + agg->aggtype); + + root->plan_params = lappend(root->plan_params, pitem); + + retval = makeNode(Param); + retval->paramkind = PARAM_EXEC; + retval->paramid = pitem->paramId; + retval->paramtype = agg->aggtype; + retval->paramtypmod = -1; + retval->paramcollid = agg->aggcollid; + retval->location = agg->location; + + return retval; +} + +/* + * Generate a Param node to replace the given GroupingFunc expression which is + * expected to have agglevelsup > 0 (ie, it is not local). + * Record the need for the GroupingFunc in the proper upper-level + * root->plan_params. + */ +Param * +replace_outer_grouping(PlannerInfo *root, GroupingFunc *grp) +{ + Param *retval; + PlannerParamItem *pitem; + Index levelsup; + Oid ptype = exprType((Node *) grp); + + Assert(grp->agglevelsup > 0 && grp->agglevelsup < root->query_level); + + /* Find the query level the GroupingFunc belongs to */ + for (levelsup = grp->agglevelsup; levelsup > 0; levelsup--) + root = root->parent_root; + + /* + * It does not seem worthwhile to try to de-duplicate references to outer + * aggs. Just make a new slot every time. + */ + grp = copyObject(grp); + IncrementVarSublevelsUp((Node *) grp, -((int) grp->agglevelsup), 0); + Assert(grp->agglevelsup == 0); + + pitem = makeNode(PlannerParamItem); + pitem->item = (Node *) grp; + pitem->paramId = list_length(root->glob->paramExecTypes); + root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes, + ptype); + + root->plan_params = lappend(root->plan_params, pitem); + + retval = makeNode(Param); + retval->paramkind = PARAM_EXEC; + retval->paramid = pitem->paramId; + retval->paramtype = ptype; + retval->paramtypmod = -1; + retval->paramcollid = InvalidOid; + retval->location = grp->location; + + return retval; +} + +/* + * Generate a Param node to replace the given Var, + * which is expected to come from some upper NestLoop plan node. + * Record the need for the Var in root->curOuterParams. + */ +Param * +replace_nestloop_param_var(PlannerInfo *root, Var *var) +{ + Param *param; + NestLoopParam *nlp; + ListCell *lc; + + /* Is this Var already listed in root->curOuterParams? */ + foreach(lc, root->curOuterParams) + { + nlp = (NestLoopParam *) lfirst(lc); + if (equal(var, nlp->paramval)) + { + /* Yes, so just make a Param referencing this NLP's slot */ + param = makeNode(Param); + param->paramkind = PARAM_EXEC; + param->paramid = nlp->paramno; + param->paramtype = var->vartype; + param->paramtypmod = var->vartypmod; + param->paramcollid = var->varcollid; + param->location = var->location; + return param; + } + } + + /* No, so assign a PARAM_EXEC slot for a new NLP */ + param = generate_new_exec_param(root, + var->vartype, + var->vartypmod, + var->varcollid); + param->location = var->location; + + /* Add it to the list of required NLPs */ + nlp = makeNode(NestLoopParam); + nlp->paramno = param->paramid; + nlp->paramval = copyObject(var); + root->curOuterParams = lappend(root->curOuterParams, nlp); + + /* And return the replacement Param */ + return param; +} + +/* + * Generate a Param node to replace the given PlaceHolderVar, + * which is expected to come from some upper NestLoop plan node. + * Record the need for the PHV in root->curOuterParams. + * + * This is just like replace_nestloop_param_var, except for PlaceHolderVars. + */ +Param * +replace_nestloop_param_placeholdervar(PlannerInfo *root, PlaceHolderVar *phv) +{ + Param *param; + NestLoopParam *nlp; + ListCell *lc; + + /* Is this PHV already listed in root->curOuterParams? */ + foreach(lc, root->curOuterParams) + { + nlp = (NestLoopParam *) lfirst(lc); + if (equal(phv, nlp->paramval)) + { + /* Yes, so just make a Param referencing this NLP's slot */ + param = makeNode(Param); + param->paramkind = PARAM_EXEC; + param->paramid = nlp->paramno; + param->paramtype = exprType((Node *) phv->phexpr); + param->paramtypmod = exprTypmod((Node *) phv->phexpr); + param->paramcollid = exprCollation((Node *) phv->phexpr); + param->location = -1; + return param; + } + } + + /* No, so assign a PARAM_EXEC slot for a new NLP */ + param = generate_new_exec_param(root, + exprType((Node *) phv->phexpr), + exprTypmod((Node *) phv->phexpr), + exprCollation((Node *) phv->phexpr)); + + /* Add it to the list of required NLPs */ + nlp = makeNode(NestLoopParam); + nlp->paramno = param->paramid; + nlp->paramval = (Var *) copyObject(phv); + root->curOuterParams = lappend(root->curOuterParams, nlp); + + /* And return the replacement Param */ + return param; +} + +/* + * 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. + * + * subplan_params is a list of PlannerParamItems that we intend to pass to + * a subquery-in-FROM. (This was constructed in root->plan_params while + * planning the subquery, but isn't there anymore when this is called.) + * + * The subplan's references to the outer variables are already represented + * as PARAM_EXEC Params, since that conversion was done by the routines above + * while planning the subquery. So we need not modify the subplan or the + * PlannerParamItems 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. This differs from replace_nestloop_param_var in + * that the PARAM_EXEC slots to use have already been determined. + * + * Note that we also use root->curOuterRels as an implicit parameter for + * sanity checks. + */ +void +process_subquery_nestloop_params(PlannerInfo *root, List *subplan_params) +{ + ListCell *lc; + + foreach(lc, subplan_params) + { + PlannerParamItem *pitem = castNode(PlannerParamItem, lfirst(lc)); + + if (IsA(pitem->item, Var)) + { + Var *var = (Var *) pitem->item; + NestLoopParam *nlp; + ListCell *lc; + + /* If not from a nestloop outer rel, complain */ + if (!bms_is_member(var->varno, root->curOuterRels)) + 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 == pitem->paramId) + { + Assert(equal(var, nlp->paramval)); + /* Present, so nothing to do */ + break; + } + } + if (lc == NULL) + { + /* No, so add it */ + nlp = makeNode(NestLoopParam); + nlp->paramno = pitem->paramId; + nlp->paramval = copyObject(var); + root->curOuterParams = lappend(root->curOuterParams, nlp); + } + } + else if (IsA(pitem->item, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) pitem->item; + NestLoopParam *nlp; + ListCell *lc; + + /* If not from a nestloop outer rel, complain */ + if (!bms_is_subset(find_placeholder_info(root, phv, false)->ph_eval_at, + root->curOuterRels)) + 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 == pitem->paramId) + { + Assert(equal(phv, nlp->paramval)); + /* Present, so nothing to do */ + break; + } + } + if (lc == NULL) + { + /* No, so add it */ + nlp = makeNode(NestLoopParam); + nlp->paramno = pitem->paramId; + nlp->paramval = (Var *) copyObject(phv); + root->curOuterParams = lappend(root->curOuterParams, nlp); + } + } + else + elog(ERROR, "unexpected type of subquery parameter"); + } +} + +/* + * Identify any NestLoopParams that should be supplied by a NestLoop plan + * node with the specified lefthand rels. Remove them from the active + * root->curOuterParams list and return them as the result list. + */ +List * +identify_current_nestloop_params(PlannerInfo *root, Relids leftrelids) +{ + List *result; + ListCell *cell; + ListCell *prev; + ListCell *next; + + result = NIL; + prev = NULL; + for (cell = list_head(root->curOuterParams); cell; cell = next) + { + NestLoopParam *nlp = (NestLoopParam *) lfirst(cell); + + next = lnext(cell); + + /* + * We are looking for Vars and PHVs that can be supplied by the + * lefthand rels. The "bms_overlap" test is just an optimization to + * allow skipping find_placeholder_info() if the PHV couldn't match. + */ + if (IsA(nlp->paramval, Var) && + bms_is_member(nlp->paramval->varno, leftrelids)) + { + root->curOuterParams = list_delete_cell(root->curOuterParams, + cell, prev); + result = lappend(result, nlp); + } + else if (IsA(nlp->paramval, PlaceHolderVar) && + bms_overlap(((PlaceHolderVar *) nlp->paramval)->phrels, + leftrelids) && + bms_is_subset(find_placeholder_info(root, + (PlaceHolderVar *) nlp->paramval, + false)->ph_eval_at, + leftrelids)) + { + root->curOuterParams = list_delete_cell(root->curOuterParams, + cell, prev); + result = lappend(result, nlp); + } + else + prev = cell; + } + return result; +} + +/* + * Generate a new Param node that will not conflict with any other. + * + * This is used to create Params representing subplan outputs or + * NestLoop parameters. + * + * We don't need to build a PlannerParamItem for such a Param, but we do + * need to make sure we record the type in paramExecTypes (otherwise, + * there won't be a slot allocated for it). + */ +Param * +generate_new_exec_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod, + Oid paramcollation) +{ + Param *retval; + + retval = makeNode(Param); + retval->paramkind = PARAM_EXEC; + retval->paramid = list_length(root->glob->paramExecTypes); + root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes, + paramtype); + retval->paramtype = paramtype; + retval->paramtypmod = paramtypmod; + retval->paramcollid = paramcollation; + retval->location = -1; + + return retval; +} + +/* + * Assign a (nonnegative) PARAM_EXEC ID for a special parameter (one that + * 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. No actual Param node + * exists with this ID, however. + */ +int +assign_special_exec_param(PlannerInfo *root) +{ + int paramId = list_length(root->glob->paramExecTypes); + + root->glob->paramExecTypes = lappend_oid(root->glob->paramExecTypes, + InvalidOid); + return paramId; +} diff --git a/src/include/optimizer/paramassign.h b/src/include/optimizer/paramassign.h new file mode 100644 index 0000000000..d18c85c938 --- /dev/null +++ b/src/include/optimizer/paramassign.h @@ -0,0 +1,34 @@ +/*------------------------------------------------------------------------- + * + * paramassign.h + * Functions for assigning PARAM_EXEC slots during planning. + * + * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/optimizer/paramassign.h + * + *------------------------------------------------------------------------- + */ +#ifndef PARAMASSIGN_H +#define PARAMASSIGN_H + +#include "nodes/relation.h" + +extern Param *replace_outer_var(PlannerInfo *root, Var *var); +extern Param *replace_outer_placeholdervar(PlannerInfo *root, + PlaceHolderVar *phv); +extern Param *replace_outer_agg(PlannerInfo *root, Aggref *agg); +extern Param *replace_outer_grouping(PlannerInfo *root, GroupingFunc *grp); +extern Param *replace_nestloop_param_var(PlannerInfo *root, Var *var); +extern Param *replace_nestloop_param_placeholdervar(PlannerInfo *root, + PlaceHolderVar *phv); +extern void process_subquery_nestloop_params(PlannerInfo *root, + List *subplan_params); +extern List *identify_current_nestloop_params(PlannerInfo *root, + Relids leftrelids); +extern Param *generate_new_exec_param(PlannerInfo *root, Oid paramtype, + int32 paramtypmod, Oid paramcollation); +extern int assign_special_exec_param(PlannerInfo *root); + +#endif /* PARAMASSIGN_H */ diff --git a/src/include/optimizer/subselect.h b/src/include/optimizer/subselect.h index 211e373430..22959ff423 100644 --- a/src/include/optimizer/subselect.h +++ b/src/include/optimizer/subselect.h @@ -1,6 +1,7 @@ /*------------------------------------------------------------------------- * * subselect.h + * Planning routines for subselects. * * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California @@ -35,9 +36,5 @@ extern Param *SS_make_initplan_output_param(PlannerInfo *root, extern void SS_make_initplan_from_plan(PlannerInfo *root, PlannerInfo *subroot, Plan *plan, Param *prm); -extern Param *assign_nestloop_param_var(PlannerInfo *root, Var *var); -extern Param *assign_nestloop_param_placeholdervar(PlannerInfo *root, - PlaceHolderVar *phv); -extern int SS_assign_special_param(PlannerInfo *root); #endif /* SUBSELECT_H */