*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.298 2007/02/19 02:23:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.299 2007/02/19 07:03:27 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
WRITE_NODE_FIELD(path_hashclauses);
}
+static void
+_outPlannerGlobal(StringInfo str, PlannerGlobal *node)
+{
+ WRITE_NODE_TYPE("PLANNERGLOBAL");
+
+ /* NB: this isn't a complete set of fields */
+ WRITE_NODE_FIELD(paramlist);
+ WRITE_INT_FIELD(next_plan_id);
+}
+
static void
_outPlannerInfo(StringInfo str, PlannerInfo *node)
{
/* NB: this isn't a complete set of fields */
WRITE_NODE_FIELD(parse);
+ WRITE_NODE_FIELD(glob);
+ WRITE_UINT_FIELD(query_level);
WRITE_NODE_FIELD(join_rel_list);
+ WRITE_NODE_FIELD(init_plans);
WRITE_NODE_FIELD(eq_classes);
WRITE_NODE_FIELD(canon_pathkeys);
WRITE_NODE_FIELD(left_join_clauses);
WRITE_OID_FIELD(parent_reloid);
}
+static void
+_outPlannerParamItem(StringInfo str, PlannerParamItem *node)
+{
+ WRITE_NODE_TYPE("PLANNERPARAMITEM");
+
+ WRITE_NODE_FIELD(item);
+ WRITE_UINT_FIELD(abslevel);
+}
+
/*****************************************************************************
*
* Stuff from parsenodes.h.
case T_HashPath:
_outHashPath(str, obj);
break;
+ case T_PlannerGlobal:
+ _outPlannerGlobal(str, obj);
+ break;
case T_PlannerInfo:
_outPlannerInfo(str, obj);
break;
case T_AppendRelInfo:
_outAppendRelInfo(str, obj);
break;
+ case T_PlannerParamItem:
+ _outPlannerParamItem(str, obj);
+ break;
case T_CreateStmt:
_outCreateStmt(str, obj);
Optimizer Data Structures
-------------------------
-PlannerInfo - global information for planning a particular Query
+PlannerGlobal - global information for a single planner invocation
+
+PlannerInfo - information for planning a particular Query (we make
+ a separate PlannerInfo node for each sub-Query)
RelOptInfo - a relation or joined relations
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.158 2007/01/28 18:50:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.159 2007/02/19 07:03:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
tuple_fraction = root->tuple_fraction;
/* Generate the plan for the subquery */
- rel->subplan = subquery_planner(subquery, tuple_fraction,
+ rel->subplan = subquery_planner(root->glob, subquery,
+ root->query_level + 1,
+ tuple_fraction,
&subquery_pathkeys);
/* Copy number of output rows from subplan */
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.83 2007/01/05 22:19:31 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.84 2007/02/19 07:03:28 tgl Exp $
*
*-------------------------------------------------------------------------
*/
else if (IsA(clause, Param))
{
/* see if we can replace the Param */
- Node *subst = estimate_expression_value(clause);
+ Node *subst = estimate_expression_value(root, clause);
if (IsA(subst, Const))
{
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.26 2007/02/06 06:50:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planagg.c,v 1.27 2007/02/19 07:03:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
memcpy(&subroot, root, sizeof(PlannerInfo));
subroot.parse = subparse = (Query *) copyObject(root->parse);
+ subroot.init_plans = NIL;
subparse->commandType = CMD_SELECT;
subparse->resultRelation = 0;
subparse->resultRelations = NIL;
info->param = SS_make_initplan_from_plan(&subroot, plan,
exprType((Node *) tle->expr),
-1);
+
+ /* Make sure the InitPlan gets into the outer list */
+ root->init_plans = list_concat(root->init_plans, subroot.init_plans);
}
/*
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.212 2007/01/20 20:45:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.213 2007/02/19 07:03:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "utils/syscache.h"
-ParamListInfo PlannerBoundParamList = NULL; /* current boundParams */
-
-
/* Expression kind codes for preprocess_expression */
#define EXPRKIND_QUAL 0
#define EXPRKIND_TARGET 1
planner(Query *parse, bool isCursor, int cursorOptions,
ParamListInfo boundParams)
{
+ PlannerGlobal *glob;
double tuple_fraction;
Plan *result_plan;
- Index save_PlannerQueryLevel;
- List *save_PlannerParamList;
- ParamListInfo save_PlannerBoundParamList;
/*
- * The planner can be called recursively (an example is when
- * eval_const_expressions tries to pre-evaluate an SQL function). So,
- * these global state variables must be saved and restored.
- *
- * Query level and the param list cannot be moved into the per-query
- * PlannerInfo structure since their whole purpose is communication across
- * multiple sub-queries. Also, boundParams is explicitly info from outside
- * the query, and so is likewise better handled as a global variable.
- *
- * Note we do NOT save and restore PlannerPlanId: it exists to assign
- * unique IDs to SubPlan nodes, and we want those IDs to be unique for the
- * life of a backend. Also, PlannerInitPlan is saved/restored in
- * subquery_planner, not here.
+ * Set up global state for this planner invocation. This data is needed
+ * across all levels of sub-Query that might exist in the given command,
+ * so we keep it in a separate struct that's linked to by each per-Query
+ * PlannerInfo.
*/
- save_PlannerQueryLevel = PlannerQueryLevel;
- save_PlannerParamList = PlannerParamList;
- save_PlannerBoundParamList = PlannerBoundParamList;
+ glob = makeNode(PlannerGlobal);
- /* Initialize state for handling outer-level references and params */
- PlannerQueryLevel = 0; /* will be 1 in top-level subquery_planner */
- PlannerParamList = NIL;
- PlannerBoundParamList = boundParams;
+ glob->boundParams = boundParams;
+ glob->paramlist = NIL;
+ glob->next_plan_id = 0;
/* Determine what fraction of the plan is likely to be scanned */
if (isCursor)
}
/* primary planning entry point (may recurse for subqueries) */
- result_plan = subquery_planner(parse, tuple_fraction, NULL);
-
- /* check we popped out the right number of levels */
- Assert(PlannerQueryLevel == 0);
+ result_plan = subquery_planner(glob, parse, 1, tuple_fraction, NULL);
/*
* If creating a plan for a scrollable cursor, make sure it can run
result_plan = set_plan_references(result_plan, parse->rtable);
/* executor wants to know total number of Params used overall */
- result_plan->nParamExec = list_length(PlannerParamList);
-
- /* restore state for outer planner, if any */
- PlannerQueryLevel = save_PlannerQueryLevel;
- PlannerParamList = save_PlannerParamList;
- PlannerBoundParamList = save_PlannerBoundParamList;
+ result_plan->nParamExec = list_length(glob->paramlist);
return result_plan;
}
* Invokes the planner on a subquery. We recurse to here for each
* sub-SELECT found in the query tree.
*
+ * glob is the global state for the current planner run.
* parse is the querytree produced by the parser & rewriter.
+ * level is the current recursion depth (1 at the top-level Query).
* tuple_fraction is the fraction of tuples we expect will be retrieved.
* tuple_fraction is interpreted as explained for grouping_planner, below.
*
*--------------------
*/
Plan *
-subquery_planner(Query *parse, double tuple_fraction,
+subquery_planner(PlannerGlobal *glob, Query *parse,
+ Index level, double tuple_fraction,
List **subquery_pathkeys)
{
- List *saved_initplan = PlannerInitPlan;
- int saved_planid = PlannerPlanId;
+ int saved_plan_id = glob->next_plan_id;
PlannerInfo *root;
Plan *plan;
List *newHaving;
ListCell *l;
- /* Set up for a new level of subquery */
- PlannerQueryLevel++;
- PlannerInitPlan = NIL;
-
/* Create a PlannerInfo data structure for this subquery */
root = makeNode(PlannerInfo);
root->parse = parse;
+ root->glob = glob;
+ root->query_level = level;
root->planner_cxt = CurrentMemoryContext;
+ root->init_plans = NIL;
root->eq_classes = NIL;
root->in_info_list = NIL;
root->append_rel_list = NIL;
* initPlan list and extParam/allParam sets for plan nodes, and attach the
* initPlans to the top plan node.
*/
- if (PlannerPlanId != saved_planid || PlannerQueryLevel > 1)
- SS_finalize_plan(plan, parse->rtable);
+ if (root->glob->next_plan_id != saved_plan_id || root->query_level > 1)
+ SS_finalize_plan(root, plan);
/* Return sort ordering info if caller wants it */
if (subquery_pathkeys)
*subquery_pathkeys = root->query_pathkeys;
- /* Return to outer subquery context */
- PlannerQueryLevel--;
- PlannerInitPlan = saved_initplan;
- /* we do NOT restore PlannerPlanId; that's not an oversight! */
-
return plan;
}
if (kind != EXPRKIND_VALUES &&
(root->parse->jointree->fromlist != NIL ||
kind == EXPRKIND_QUAL ||
- PlannerQueryLevel > 1))
+ root->query_level > 1))
expr = eval_const_expressions(expr);
/*
/* Expand SubLinks to SubPlans */
if (root->parse->hasSubLinks)
- expr = SS_process_sublinks(expr, (kind == EXPRKIND_QUAL));
+ expr = SS_process_sublinks(root, expr, (kind == EXPRKIND_QUAL));
/*
* XXX do not insert anything here unless you have grokked the comments in
*/
/* Replace uplevel vars with Param nodes (this IS possible in VALUES) */
- if (PlannerQueryLevel > 1)
- expr = SS_replace_correlation_vars(expr);
+ if (root->query_level > 1)
+ expr = SS_replace_correlation_vars(root, expr);
/*
* If it's a qual or havingQual, convert it to implicit-AND format. (We
subroot.in_info_list = (List *)
adjust_appendrel_attrs((Node *) root->in_info_list,
appinfo);
+ subroot.init_plans = NIL;
/* There shouldn't be any OJ info to translate, as yet */
Assert(subroot.oj_info_list == NIL);
subplans = lappend(subplans, subplan);
+ /* Make sure any initplans from this rel get into the outer list */
+ root->init_plans = list_concat(root->init_plans, subroot.init_plans);
+
/* Build target-relations list for the executor */
resultRelations = lappend_int(resultRelations, appinfo->child_relid);
*/
if (parse->limitCount)
{
- est = estimate_expression_value(parse->limitCount);
+ est = estimate_expression_value(root, parse->limitCount);
if (est && IsA(est, Const))
{
if (((Const *) est)->constisnull)
if (parse->limitOffset)
{
- est = estimate_expression_value(parse->limitOffset);
+ est = estimate_expression_value(root, parse->limitOffset);
if (est && IsA(est, Const))
{
if (((Const *) est)->constisnull)
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.119 2007/02/19 02:23:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.120 2007/02/19 07:03:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "utils/syscache.h"
-Index PlannerQueryLevel; /* level of current query */
-List *PlannerInitPlan; /* init subplans for current query */
-List *PlannerParamList; /* to keep track of cross-level Params */
-
-int PlannerPlanId = 0; /* to assign unique ID to subquery plans */
-
-/*
- * PlannerParamList keeps track of the PARAM_EXEC slots that we have decided
- * we need for the query. At runtime these slots are used to pass values
- * either down into subqueries (for outer references in subqueries) or up out
- * of subqueries (for the results of a subplan). The n'th entry in the list
- * (n counts from 0) corresponds to Param->paramid = n.
- *
- * Each ParamList item shows the absolute query level it is associated with,
- * where the outermost query is level 1 and nested subqueries have higher
- * numbers. The item the parameter slot represents can be one of three kinds:
- *
- * A Var: the slot represents a variable of that level that must be passed
- * down because subqueries have outer references to it. The varlevelsup
- * value in the Var will always be zero.
- *
- * An Aggref (with an expression tree representing its argument): the slot
- * represents an aggregate expression that is an outer reference for some
- * subquery. The Aggref itself has agglevelsup = 0, and its argument tree
- * is adjusted to match in level.
- *
- * A Param: the slot holds the result of a subplan (it is a setParam item
- * for that subplan). The absolute level shown for such items corresponds
- * to the parent query of the subplan.
- *
- * Note: we detect duplicate Var parameters and coalesce them into one slot,
- * but we do not do this for Aggref or Param slots.
- */
-typedef struct PlannerParamItem
-{
- Node *item; /* the Var, Aggref, or Param */
- Index abslevel; /* its absolute query level */
-} PlannerParamItem;
-
-
typedef struct convert_testexpr_context
{
+ PlannerInfo *root;
int rtindex; /* RT index for Vars, or 0 for Params */
List *righthandIds; /* accumulated list of Vars or Param IDs */
} convert_testexpr_context;
+typedef struct process_sublinks_context
+{
+ PlannerInfo *root;
+ bool isTopQual;
+} process_sublinks_context;
+
typedef struct finalize_primnode_context
{
Bitmapset *paramids; /* Set of PARAM_EXEC paramids found */
} finalize_primnode_context;
-static Node *convert_testexpr(Node *testexpr,
- int rtindex,
- List **righthandIds);
+static Node *convert_testexpr(PlannerInfo *root,
+ Node *testexpr,
+ int rtindex,
+ List **righthandIds);
static Node *convert_testexpr_mutator(Node *node,
convert_testexpr_context *context);
static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
static bool hash_ok_operator(OpExpr *expr);
-static Node *replace_correlation_vars_mutator(Node *node, void *context);
-static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
+static Node *replace_correlation_vars_mutator(Node *node, PlannerInfo *root);
+static Node *process_sublinks_mutator(Node *node,
+ process_sublinks_context *context);
static Bitmapset *finalize_plan(Plan *plan, List *rtable,
Bitmapset *outer_params,
Bitmapset *valid_params);
* which is expected to have varlevelsup > 0 (ie, it is not local).
*/
static Param *
-replace_outer_var(Var *var)
+replace_outer_var(PlannerInfo *root, Var *var)
{
Param *retval;
ListCell *ppl;
Index abslevel;
int i;
- Assert(var->varlevelsup > 0 && var->varlevelsup < PlannerQueryLevel);
- abslevel = PlannerQueryLevel - var->varlevelsup;
+ Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level);
+ abslevel = root->query_level - var->varlevelsup;
/*
- * If there's already a PlannerParamList entry for this same Var, just use
+ * If there's already a paramlist entry for this same Var, just use
* it. NOTE: in sufficiently complex querytrees, it is possible for the
* same varno/abslevel to refer to different RTEs in different parts of
* the parsetree, so that different fields might end up sharing the same
* a subplan's args list.
*/
i = 0;
- foreach(ppl, PlannerParamList)
+ foreach(ppl, root->glob->paramlist)
{
pitem = (PlannerParamItem *) lfirst(ppl);
if (pitem->abslevel == abslevel && IsA(pitem->item, Var))
var = (Var *) copyObject(var);
var->varlevelsup = 0;
- pitem = (PlannerParamItem *) palloc(sizeof(PlannerParamItem));
+ pitem = makeNode(PlannerParamItem);
pitem->item = (Node *) var;
pitem->abslevel = abslevel;
- PlannerParamList = lappend(PlannerParamList, pitem);
+ root->glob->paramlist = lappend(root->glob->paramlist, pitem);
/* i is already the correct index for the new item */
}
* which is expected to have agglevelsup > 0 (ie, it is not local).
*/
static Param *
-replace_outer_agg(Aggref *agg)
+replace_outer_agg(PlannerInfo *root, Aggref *agg)
{
Param *retval;
PlannerParamItem *pitem;
Index abslevel;
int i;
- Assert(agg->agglevelsup > 0 && agg->agglevelsup < PlannerQueryLevel);
- abslevel = PlannerQueryLevel - agg->agglevelsup;
+ Assert(agg->agglevelsup > 0 && agg->agglevelsup < root->query_level);
+ abslevel = root->query_level - agg->agglevelsup;
/*
* It does not seem worthwhile to try to match duplicate outer aggs. Just
IncrementVarSublevelsUp((Node *) agg, -((int) agg->agglevelsup), 0);
Assert(agg->agglevelsup == 0);
- pitem = (PlannerParamItem *) palloc(sizeof(PlannerParamItem));
+ pitem = makeNode(PlannerParamItem);
pitem->item = (Node *) agg;
pitem->abslevel = abslevel;
- PlannerParamList = lappend(PlannerParamList, pitem);
- i = list_length(PlannerParamList) - 1;
+ root->glob->paramlist = lappend(root->glob->paramlist, pitem);
+ i = list_length(root->glob->paramlist) - 1;
retval = makeNode(Param);
retval->paramkind = PARAM_EXEC;
* This is used to allocate PARAM_EXEC slots for subplan outputs.
*/
static Param *
-generate_new_param(Oid paramtype, int32 paramtypmod)
+generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod)
{
Param *retval;
PlannerParamItem *pitem;
retval = makeNode(Param);
retval->paramkind = PARAM_EXEC;
- retval->paramid = list_length(PlannerParamList);
+ retval->paramid = list_length(root->glob->paramlist);
retval->paramtype = paramtype;
retval->paramtypmod = paramtypmod;
- pitem = (PlannerParamItem *) palloc(sizeof(PlannerParamItem));
+ pitem = makeNode(PlannerParamItem);
pitem->item = (Node *) retval;
- pitem->abslevel = PlannerQueryLevel;
+ pitem->abslevel = root->query_level;
- PlannerParamList = lappend(PlannerParamList, pitem);
+ root->glob->paramlist = lappend(root->glob->paramlist, pitem);
return retval;
}
* tree containing InitPlan Param nodes.
*/
static Node *
-make_subplan(SubLink *slink, Node *testexpr, bool isTopQual)
+make_subplan(PlannerInfo *root, SubLink *slink, Node *testexpr, bool isTopQual)
{
SubPlan *node = makeNode(SubPlan);
Query *subquery = (Query *) (slink->subselect);
/*
* Generate the plan for the subquery.
*/
- node->plan = plan = subquery_planner(subquery, tuple_fraction, NULL);
+ node->plan = plan = subquery_planner(root->glob, subquery,
+ root->query_level + 1,
+ tuple_fraction,
+ NULL);
- node->plan_id = PlannerPlanId++; /* Assign unique ID to this SubPlan */
+ /* Assign quasi-unique ID to this SubPlan */
+ node->plan_id = root->glob->next_plan_id++;
node->rtable = subquery->rtable;
tmpset = bms_copy(plan->extParam);
while ((paramid = bms_first_member(tmpset)) >= 0)
{
- PlannerParamItem *pitem = list_nth(PlannerParamList, paramid);
+ PlannerParamItem *pitem = list_nth(root->glob->paramlist, paramid);
- if (pitem->abslevel == PlannerQueryLevel)
+ if (pitem->abslevel == root->query_level)
node->parParam = lappend_int(node->parParam, paramid);
}
bms_free(tmpset);
{
Param *prm;
- prm = generate_new_param(BOOLOID, -1);
+ prm = generate_new_param(root, BOOLOID, -1);
node->setParam = list_make1_int(prm->paramid);
- PlannerInitPlan = lappend(PlannerInitPlan, node);
+ root->init_plans = lappend(root->init_plans, node);
result = (Node *) prm;
}
else if (node->parParam == NIL && slink->subLinkType == EXPR_SUBLINK)
Param *prm;
Assert(!te->resjunk);
- prm = generate_new_param(exprType((Node *) te->expr),
+ prm = generate_new_param(root,
+ exprType((Node *) te->expr),
exprTypmod((Node *) te->expr));
node->setParam = list_make1_int(prm->paramid);
- PlannerInitPlan = lappend(PlannerInitPlan, node);
+ root->init_plans = lappend(root->init_plans, node);
result = (Node *) prm;
}
else if (node->parParam == NIL && slink->subLinkType == ARRAY_SUBLINK)
if (!OidIsValid(arraytype))
elog(ERROR, "could not find array type for datatype %s",
format_type_be(exprType((Node *) te->expr)));
- prm = generate_new_param(arraytype, exprTypmod((Node *) te->expr));
+ prm = generate_new_param(root,
+ arraytype,
+ exprTypmod((Node *) te->expr));
node->setParam = list_make1_int(prm->paramid);
- PlannerInitPlan = lappend(PlannerInitPlan, node);
+ root->init_plans = lappend(root->init_plans, node);
result = (Node *) prm;
}
else if (node->parParam == NIL && slink->subLinkType == ROWCOMPARE_SUBLINK)
{
/* Adjust the Params */
- result = convert_testexpr(testexpr,
+ result = convert_testexpr(root,
+ testexpr,
0,
&node->paramIds);
node->setParam = list_copy(node->paramIds);
- PlannerInitPlan = lappend(PlannerInitPlan, node);
+ root->init_plans = lappend(root->init_plans, node);
/*
* The executable expression is returned to become part of the outer
ListCell *l;
/* Adjust the Params */
- node->testexpr = convert_testexpr(testexpr,
+ node->testexpr = convert_testexpr(root,
+ testexpr,
0,
&node->paramIds);
args = NIL;
foreach(l, node->parParam)
{
- PlannerParamItem *pitem = list_nth(PlannerParamList, lfirst_int(l));
+ PlannerParamItem *pitem = list_nth(root->glob->paramlist,
+ lfirst_int(l));
/*
* The Var or Aggref has already been adjusted to have the correct
* any we find are for our own level of SubLink.
*/
static Node *
-convert_testexpr(Node *testexpr,
+convert_testexpr(PlannerInfo *root,
+ Node *testexpr,
int rtindex,
List **righthandIds)
{
Node *result;
convert_testexpr_context context;
+ context.root = root;
context.rtindex = rtindex;
context.righthandIds = NIL;
result = convert_testexpr_mutator(testexpr, &context);
/* Make the Param node representing the subplan's result */
Param *newparam;
- newparam = generate_new_param(param->paramtype,
+ newparam = generate_new_param(context->root,
+ param->paramtype,
param->paramtypmod);
/* Record its ID */
context->righthandIds = lappend_int(context->righthandIds,
* ininfo->sub_targetlist is filled with a list of Vars representing the
* subselect outputs.
*/
- result = convert_testexpr(sublink->testexpr,
+ result = convert_testexpr(root,
+ sublink->testexpr,
rtindex,
&ininfo->sub_targetlist);
* argument expressions, either in the parent or the child level.
*/
Node *
-SS_replace_correlation_vars(Node *expr)
+SS_replace_correlation_vars(PlannerInfo *root, Node *expr)
{
/* No setup needed for tree walk, so away we go */
- return replace_correlation_vars_mutator(expr, NULL);
+ return replace_correlation_vars_mutator(expr, root);
}
static Node *
-replace_correlation_vars_mutator(Node *node, void *context)
+replace_correlation_vars_mutator(Node *node, PlannerInfo *root)
{
if (node == NULL)
return NULL;
if (IsA(node, Var))
{
if (((Var *) node)->varlevelsup > 0)
- return (Node *) replace_outer_var((Var *) node);
+ return (Node *) replace_outer_var(root, (Var *) node);
}
if (IsA(node, Aggref))
{
if (((Aggref *) node)->agglevelsup > 0)
- return (Node *) replace_outer_agg((Aggref *) node);
+ return (Node *) replace_outer_agg(root, (Aggref *) node);
}
return expression_tree_mutator(node,
replace_correlation_vars_mutator,
- context);
+ (void *) root);
}
/*
* not distinguish FALSE from UNKNOWN return values.
*/
Node *
-SS_process_sublinks(Node *expr, bool isQual)
+SS_process_sublinks(PlannerInfo *root, Node *expr, bool isQual)
{
- /* The only context needed is the initial are-we-in-a-qual flag */
- return process_sublinks_mutator(expr, &isQual);
+ process_sublinks_context context;
+
+ context.root = root;
+ context.isTopQual = isQual;
+ return process_sublinks_mutator(expr, &context);
}
static Node *
-process_sublinks_mutator(Node *node, bool *isTopQual)
+process_sublinks_mutator(Node *node, process_sublinks_context *context)
{
- bool locTopQual;
+ process_sublinks_context locContext;
+
+ locContext.root = context->root;
if (node == NULL)
return NULL;
/*
* First, recursively process the lefthand-side expressions, if any.
+ * They're not top-level anymore.
*/
- locTopQual = false;
- testexpr = process_sublinks_mutator(sublink->testexpr, &locTopQual);
+ locContext.isTopQual = false;
+ testexpr = process_sublinks_mutator(sublink->testexpr, &locContext);
/*
* Now build the SubPlan node and make the expr to return.
*/
- return make_subplan(sublink, testexpr, *isTopQual);
+ return make_subplan(context->root,
+ sublink,
+ testexpr,
+ context->isTopQual);
}
/*
ListCell *l;
/* Still at qual top-level */
- locTopQual = *isTopQual;
+ locContext.isTopQual = context->isTopQual;
foreach(l, ((BoolExpr *) node)->args)
{
Node *newarg;
- newarg = process_sublinks_mutator(lfirst(l),
- (void *) &locTopQual);
+ newarg = process_sublinks_mutator(lfirst(l), &locContext);
if (and_clause(newarg))
newargs = list_concat(newargs, ((BoolExpr *) newarg)->args);
else
}
/* otherwise not at qual top-level */
- locTopQual = false;
+ locContext.isTopQual = false;
if (or_clause(node))
{
{
Node *newarg;
- newarg = process_sublinks_mutator(lfirst(l),
- (void *) &locTopQual);
+ newarg = process_sublinks_mutator(lfirst(l), &locContext);
if (or_clause(newarg))
newargs = list_concat(newargs, ((BoolExpr *) newarg)->args);
else
return expression_tree_mutator(node,
process_sublinks_mutator,
- (void *) &locTopQual);
+ (void *) &locContext);
}
/*
* to the top plan node.
*/
void
-SS_finalize_plan(Plan *plan, List *rtable)
+SS_finalize_plan(PlannerInfo *root, Plan *plan)
{
Bitmapset *outer_params,
*valid_params,
*/
outer_params = valid_params = NULL;
paramid = 0;
- foreach(l, PlannerParamList)
+ foreach(l, root->glob->paramlist)
{
PlannerParamItem *pitem = (PlannerParamItem *) lfirst(l);
- if (pitem->abslevel < PlannerQueryLevel)
+ if (pitem->abslevel < root->query_level)
{
/* valid outer-level parameter */
outer_params = bms_add_member(outer_params, paramid);
valid_params = bms_add_member(valid_params, paramid);
}
- else if (pitem->abslevel == PlannerQueryLevel &&
+ else if (pitem->abslevel == root->query_level &&
IsA(pitem->item, Param))
{
/* valid local parameter (i.e., a setParam of my child) */
/*
* Now recurse through plan tree.
*/
- (void) finalize_plan(plan, rtable, outer_params, valid_params);
+ (void) finalize_plan(plan, root->parse->rtable, outer_params, valid_params);
bms_free(outer_params);
bms_free(valid_params);
* top node. This is a conservative overestimate, since in fact each
* initPlan might be executed later than plan startup, or even not at all.
*/
- plan->initPlan = PlannerInitPlan;
- PlannerInitPlan = NIL; /* make sure they're not attached twice */
+ plan->initPlan = root->init_plans;
+ root->init_plans = NIL; /* make sure they're not attached twice */
initExtParam = initSetParam = NULL;
initplan_cost = 0;
SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
Oid resulttype, int32 resulttypmod)
{
- List *saved_initplan = PlannerInitPlan;
+ List *saved_init_plans;
SubPlan *node;
Param *prm;
/*
* Set up for a new level of subquery. This is just to keep
- * SS_finalize_plan from becoming confused.
+ * SS_finalize_plan from becoming confused; we don't bother with making
+ * a whole new PlannerInfo struct.
*/
- PlannerQueryLevel++;
- PlannerInitPlan = NIL;
+ root->query_level++;
+ saved_init_plans = root->init_plans;
+ root->init_plans = NIL;
/*
* Build extParam/allParam sets for plan nodes.
*/
- SS_finalize_plan(plan, root->parse->rtable);
+ SS_finalize_plan(root, plan);
/* Return to outer subquery context */
- PlannerQueryLevel--;
- PlannerInitPlan = saved_initplan;
+ root->query_level--;
+ root->init_plans = saved_init_plans;
/*
* Create a SubPlan node and add it to the outer list of InitPlans.
node = makeNode(SubPlan);
node->subLinkType = EXPR_SUBLINK;
node->plan = plan;
- node->plan_id = PlannerPlanId++; /* Assign unique ID to this SubPlan */
+ /* Assign quasi-unique ID to this SubPlan */
+ node->plan_id = root->glob->next_plan_id++;
node->rtable = root->parse->rtable;
- PlannerInitPlan = lappend(PlannerInitPlan, node);
+ root->init_plans = lappend(root->init_plans, node);
/*
* The node can't have any inputs (since it's an initplan), so the
/*
* Make a Param that will be the subplan's output.
*/
- prm = generate_new_param(resulttype, resulttypmod);
+ prm = generate_new_param(root, resulttype, resulttypmod);
node->setParam = list_make1_int(prm->paramid);
return prm;
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.46 2007/01/20 20:45:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.47 2007/02/19 07:03:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
subroot = makeNode(PlannerInfo);
subroot->parse = subquery;
+ subroot->glob = root->glob;
+ subroot->query_level = root->query_level;
subroot->planner_cxt = CurrentMemoryContext;
+ subroot->init_plans = NIL;
subroot->in_info_list = NIL;
subroot->append_rel_list = NIL;
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.85 2007/01/05 22:19:32 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.86 2007/02/19 07:03:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* Currently the executor only supports FOR UPDATE/SHARE at top level
*/
- if (PlannerQueryLevel > 1)
+ if (root->query_level > 1)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("SELECT FOR UPDATE/SHARE is not allowed in subqueries")));
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.137 2007/01/22 20:00:39 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.138 2007/02/19 07:03:30 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* Generate plan for primitive subquery
*/
- subplan = subquery_planner(subquery, tuple_fraction, NULL);
+ subplan = subquery_planner(root->glob, subquery,
+ root->query_level + 1,
+ tuple_fraction,
+ NULL);
/*
* Add a SubqueryScan with the caller-requested targetlist
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.234 2007/02/16 23:32:08 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.235 2007/02/19 07:03:30 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
typedef struct
{
+ ParamListInfo boundParams;
List *active_fns;
Node *case_val;
bool estimate;
{
eval_const_expressions_context context;
+ context.boundParams = NULL; /* don't use any bound params */
context.active_fns = NIL; /* nothing being recursively simplified */
context.case_val = NULL; /* no CASE being examined */
context.estimate = false; /* safe transformations only */
*--------------------
*/
Node *
-estimate_expression_value(Node *node)
+estimate_expression_value(PlannerInfo *root, Node *node)
{
eval_const_expressions_context context;
+ context.boundParams = root->glob->boundParams; /* bound Params */
context.active_fns = NIL; /* nothing being recursively simplified */
context.case_val = NULL; /* no CASE being examined */
context.estimate = true; /* unsafe transformations OK */
/* Look to see if we've been given a value for this Param */
if (param->paramkind == PARAM_EXTERN &&
- PlannerBoundParamList != NULL &&
+ context->boundParams != NULL &&
param->paramid > 0 &&
- param->paramid <= PlannerBoundParamList->numParams)
+ param->paramid <= context->boundParams->numParams)
{
- ParamExternData *prm = &PlannerBoundParamList->params[param->paramid - 1];
+ ParamExternData *prm = &context->boundParams->params[param->paramid - 1];
if (OidIsValid(prm->ptype))
{
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.225 2007/01/31 16:54:51 teodor Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.226 2007/02/19 07:03:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
if (vardata->rel && rdata.rel == NULL)
{
*varonleft = true;
- *other = estimate_expression_value(rdata.var);
+ *other = estimate_expression_value(root, rdata.var);
/* Assume we need no ReleaseVariableStats(rdata) here */
return true;
}
if (vardata->rel == NULL && rdata.rel)
{
*varonleft = false;
- *other = estimate_expression_value(vardata->var);
+ *other = estimate_expression_value(root, vardata->var);
/* Assume we need no ReleaseVariableStats(*vardata) here */
*vardata = rdata;
return true;
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.194 2007/02/03 14:06:55 petere Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.195 2007/02/19 07:03:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
* TAGS FOR PLANNER NODES (relation.h)
*/
T_PlannerInfo = 500,
+ T_PlannerGlobal,
T_RelOptInfo,
T_IndexOptInfo,
T_Path,
T_OuterJoinInfo,
T_InClauseInfo,
T_AppendRelInfo,
+ T_PlannerParamItem,
/*
* TAGS FOR MEMORY NODES (memnodes.h)
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.124 2007/02/03 14:06:56 petere Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.125 2007/02/19 07:03:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
List *paramIds; /* IDs of Params embedded in the above */
/* The subselect, transformed to a Plan: */
struct Plan *plan; /* subselect plan itself */
- int plan_id; /* dummy thing because of we haven't equal
- * funcs for plan nodes... actually, we could
- * put *plan itself somewhere else (TopPlan
- * node ?)... */
+ int plan_id; /* kluge because we haven't equal-funcs for
+ * plan nodes... we compare this instead of
+ * subselect plan */
List *rtable; /* range table for subselect */
/* Information about execution strategy: */
bool useHashTable; /* TRUE to store subselect output in a hash
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.135 2007/02/16 20:57:19 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.136 2007/02/19 07:03:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "access/sdir.h"
#include "nodes/bitmapset.h"
+#include "nodes/params.h"
#include "nodes/parsenodes.h"
#include "storage/block.h"
} QualCost;
+/*----------
+ * PlannerGlobal
+ * Global information for planning/optimization
+ *
+ * PlannerGlobal holds state for an entire planner invocation; this state
+ * is shared across all levels of sub-Queries that exist in the command being
+ * planned.
+ *----------
+ */
+typedef struct PlannerGlobal
+{
+ NodeTag type;
+
+ ParamListInfo boundParams; /* Param values provided to planner() */
+
+ List *paramlist; /* to keep track of cross-level Params */
+
+ int next_plan_id; /* hack for distinguishing SubPlans */
+} PlannerGlobal;
+
+
/*----------
* PlannerInfo
* Per-query information for planning/optimization
Query *parse; /* the Query being planned */
+ PlannerGlobal *glob; /* global info for current planner run */
+
+ Index query_level; /* 1 at the outermost Query */
+
/*
* simple_rel_array holds pointers to "base rels" and "other rels" (see
* comments for RelOptInfo for more info). It is indexed by rangetable
List *join_rel_list; /* list of join-relation RelOptInfos */
struct HTAB *join_rel_hash; /* optional hashtable for join relations */
+ List *init_plans; /* init subplans for query */
+
List *eq_classes; /* list of active EquivalenceClasses */
List *canon_pathkeys; /* list of "canonical" PathKeys */
Oid parent_reloid; /* OID of parent relation */
} AppendRelInfo;
+/*
+ * glob->paramlist keeps track of the PARAM_EXEC slots that we have decided
+ * we need for the query. At runtime these slots are used to pass values
+ * either down into subqueries (for outer references in subqueries) or up out
+ * of subqueries (for the results of a subplan). The n'th entry in the list
+ * (n counts from 0) corresponds to Param->paramid = n.
+ *
+ * Each paramlist item shows the absolute query level it is associated with,
+ * where the outermost query is level 1 and nested subqueries have higher
+ * numbers. The item the parameter slot represents can be one of three kinds:
+ *
+ * A Var: the slot represents a variable of that level that must be passed
+ * down because subqueries have outer references to it. The varlevelsup
+ * value in the Var will always be zero.
+ *
+ * An Aggref (with an expression tree representing its argument): the slot
+ * represents an aggregate expression that is an outer reference for some
+ * subquery. The Aggref itself has agglevelsup = 0, and its argument tree
+ * is adjusted to match in level.
+ *
+ * A Param: the slot holds the result of a subplan (it is a setParam item
+ * for that subplan). The absolute level shown for such items corresponds
+ * to the parent query of the subplan.
+ *
+ * Note: we detect duplicate Var parameters and coalesce them into one slot,
+ * but we do not do this for Aggref or Param slots.
+ */
+typedef struct PlannerParamItem
+{
+ NodeTag type;
+
+ Node *item; /* the Var, Aggref, or Param */
+ Index abslevel; /* its absolute query level */
+} PlannerParamItem;
+
#endif /* RELATION_H */
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.86 2007/01/22 01:35:22 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.87 2007/02/19 07:03:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern Node *eval_const_expressions(Node *node);
-extern Node *estimate_expression_value(Node *node);
+extern Node *estimate_expression_value(PlannerInfo *root, Node *node);
extern bool expression_tree_walker(Node *node, bool (*walker) (),
void *context);
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/optimizer/planner.h,v 1.36 2007/01/05 22:19:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/planner.h,v 1.37 2007/02/19 07:03:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef PLANNER_H
#define PLANNER_H
-#include "nodes/params.h"
-#include "nodes/parsenodes.h"
#include "nodes/plannodes.h"
+#include "nodes/relation.h"
-extern ParamListInfo PlannerBoundParamList; /* current boundParams */
-
extern Plan *planner(Query *parse, bool isCursor, int cursorOptions,
ParamListInfo boundParams);
-extern Plan *subquery_planner(Query *parse, double tuple_fraction,
- List **subquery_pathkeys);
+extern Plan *subquery_planner(PlannerGlobal *glob, Query *parse,
+ Index level, double tuple_fraction,
+ List **subquery_pathkeys);
#endif /* PLANNER_H */
* Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/optimizer/subselect.h,v 1.28 2007/01/05 22:19:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/optimizer/subselect.h,v 1.29 2007/02/19 07:03:34 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "nodes/plannodes.h"
#include "nodes/relation.h"
-
-extern Index PlannerQueryLevel; /* level of current query */
-extern List *PlannerInitPlan; /* init subplans for current query */
-extern List *PlannerParamList; /* to keep track of cross-level Params */
-extern int PlannerPlanId; /* to assign unique ID to subquery plans */
-
extern Node *convert_IN_to_join(PlannerInfo *root, SubLink *sublink);
-extern Node *SS_replace_correlation_vars(Node *expr);
-extern Node *SS_process_sublinks(Node *expr, bool isQual);
-extern void SS_finalize_plan(Plan *plan, List *rtable);
+extern Node *SS_replace_correlation_vars(PlannerInfo *root, Node *expr);
+extern Node *SS_process_sublinks(PlannerInfo *root, Node *expr, bool isQual);
+extern void SS_finalize_plan(PlannerInfo *root, Plan *plan);
extern Param *SS_make_initplan_from_plan(PlannerInfo *root, Plan *plan,
Oid resulttype, int32 resulttypmod);