#include <limits.h>
#include <math.h>
-#include "access/stratnum.h"
#include "access/sysattr.h"
#include "catalog/pg_class.h"
#include "foreign/fdwapi.h"
#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"
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);
/* 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;
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);
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);
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);
/*
* 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,
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);
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
#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"
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;
returningLists,
rowMarks,
NULL,
- SS_assign_special_param(root)));
+ assign_special_exec_param(root)));
}
/*--------------------
{
path = (Path *) create_lockrows_path(root, final_rel, path,
root->rowMarks,
- SS_assign_special_param(root));
+ assign_special_exec_param(root));
}
/*
returningLists,
rowMarks,
parse->onConflict,
- SS_assign_special_param(root));
+ assign_special_exec_param(root));
}
/* And shove it into final_rel */
/*-------------------------------------------------------------------------
*
* 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
#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"
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.
*
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;
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;
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;
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);
}
* 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);
/*
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++,
* 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)
{
Oid resulttype, int32 resulttypmod,
Oid resultcollation)
{
- return generate_new_param(root, resulttype, resulttypmod, resultcollation);
+ return generate_new_exec_param(root, resulttype,
+ resulttypmod, resultcollation);
}
/*
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
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
/*-------------------------------------------------------------------------
*
* 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
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 */