aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan')
-rw-r--r--src/backend/optimizer/plan/createplan.c70
-rw-r--r--src/backend/optimizer/plan/planmain.c42
-rw-r--r--src/backend/optimizer/plan/planner.c142
-rw-r--r--src/backend/optimizer/plan/setrefs.c12
-rw-r--r--src/backend/optimizer/plan/subselect.c63
5 files changed, 187 insertions, 142 deletions
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index cc308b4fc96..eb005121cd5 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.97 2000/09/29 18:21:33 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.98 2000/10/05 19:11:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -68,8 +68,6 @@ static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
ScanDirection indexscandir);
static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
List *tideval);
-static SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
- Index scanrelid, Plan *subplan);
static NestLoop *make_nestloop(List *tlist,
List *joinclauses, List *otherclauses,
Plan *lefttree, Plan *righttree,
@@ -86,7 +84,6 @@ static MergeJoin *make_mergejoin(List *tlist,
Plan *lefttree, Plan *righttree,
JoinType jointype);
static void copy_path_costsize(Plan *dest, Path *src);
-static void copy_plan_costsize(Plan *dest, Plan *src);
/*
* create_plan
@@ -1109,7 +1106,7 @@ copy_path_costsize(Plan *dest, Path *src)
* but it helps produce more reasonable-looking EXPLAIN output.
* (Some callers alter the info after copying it.)
*/
-static void
+void
copy_plan_costsize(Plan *dest, Plan *src)
{
if (src)
@@ -1206,7 +1203,7 @@ make_tidscan(List *qptlist,
return node;
}
-static SubqueryScan *
+SubqueryScan *
make_subqueryscan(List *qptlist,
List *qpqual,
Index scanrelid,
@@ -1593,6 +1590,67 @@ make_unique(List *tlist, Plan *lefttree, List *distinctList)
return node;
}
+/*
+ * distinctList is a list of SortClauses, identifying the targetlist items
+ * that should be considered by the SetOp filter.
+ */
+
+SetOp *
+make_setop(SetOpCmd cmd, List *tlist, Plan *lefttree,
+ List *distinctList, AttrNumber flagColIdx)
+{
+ SetOp *node = makeNode(SetOp);
+ Plan *plan = &node->plan;
+ int numCols = length(distinctList);
+ int keyno = 0;
+ AttrNumber *dupColIdx;
+ List *slitem;
+
+ copy_plan_costsize(plan, lefttree);
+
+ /*
+ * Charge one cpu_operator_cost per comparison per input tuple. We
+ * assume all columns get compared at most of the tuples.
+ */
+ plan->total_cost += cpu_operator_cost * plan->plan_rows * numCols;
+
+ /*
+ * As for Group, we make the unsupported assumption that there will be
+ * 10% as many tuples out as in.
+ */
+ plan->plan_rows *= 0.1;
+ if (plan->plan_rows < 1)
+ plan->plan_rows = 1;
+
+ plan->state = (EState *) NULL;
+ plan->targetlist = tlist;
+ plan->qual = NIL;
+ plan->lefttree = lefttree;
+ plan->righttree = NULL;
+
+ /*
+ * convert SortClause list into array of attr indexes, as wanted by
+ * exec
+ */
+ Assert(numCols > 0);
+ dupColIdx = (AttrNumber *) palloc(sizeof(AttrNumber) * numCols);
+
+ foreach(slitem, distinctList)
+ {
+ SortClause *sortcl = (SortClause *) lfirst(slitem);
+ TargetEntry *tle = get_sortgroupclause_tle(sortcl, tlist);
+
+ dupColIdx[keyno++] = tle->resdom->resno;
+ }
+
+ node->cmd = cmd;
+ node->numCols = numCols;
+ node->dupColIdx = dupColIdx;
+ node->flagColIdx = flagColIdx;
+
+ return node;
+}
+
Result *
make_result(List *tlist,
Node *resconstantqual,
diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c
index 29cfccfef7b..a9747b32799 100644
--- a/src/backend/optimizer/plan/planmain.c
+++ b/src/backend/optimizer/plan/planmain.c
@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.60 2000/09/29 18:21:33 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.61 2000/10/05 19:11:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -174,8 +174,6 @@ subplanner(Query *root,
List *brel;
RelOptInfo *final_rel;
Plan *resultplan;
- MemoryContext mycontext;
- MemoryContext oldcxt;
Path *cheapestpath;
Path *presortedpath;
@@ -228,24 +226,6 @@ subplanner(Query *root,
root->query_pathkeys = canonicalize_pathkeys(root, root->query_pathkeys);
/*
- * We might allocate quite a lot of storage during planning (due to
- * constructing lots of Paths), but all of it can be reclaimed after
- * we generate the finished Plan tree. Work in a temporary context
- * to let that happen. We make the context a child of
- * TransactionCommandContext so it will be freed if error abort.
- *
- * Note: beware of trying to move this up to the start of this routine.
- * Some of the data structures built above --- notably the pathkey
- * equivalence sets --- will still be needed after this routine exits.
- */
- mycontext = AllocSetContextCreate(TransactionCommandContext,
- "Planner",
- ALLOCSET_DEFAULT_MINSIZE,
- ALLOCSET_DEFAULT_INITSIZE,
- ALLOCSET_DEFAULT_MAXSIZE);
- oldcxt = MemoryContextSwitchTo(mycontext);
-
- /*
* Ready to do the primary planning.
*/
final_rel = make_one_rel(root);
@@ -355,25 +335,5 @@ subplanner(Query *root,
plan_built:
- /*
- * Must copy the completed plan tree and its pathkeys out of temporary
- * context. We also have to copy the rtable in case it contains any
- * subqueries. (If it does, they'll have been modified during the
- * recursive invocation of planner.c, and hence will contain substructure
- * allocated in my temporary context...)
- */
- MemoryContextSwitchTo(oldcxt);
-
- resultplan = copyObject(resultplan);
-
- root->query_pathkeys = copyObject(root->query_pathkeys);
-
- root->rtable = copyObject(root->rtable);
-
- /*
- * Now we can release the Path storage.
- */
- MemoryContextDelete(mycontext);
-
return resultplan;
}
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 937628121b3..d73ca9a34ac 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.91 2000/09/29 18:21:33 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.92 2000/10/05 19:11:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -48,7 +48,6 @@ static List *make_subplanTargetList(Query *parse, List *tlist,
static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
List *groupClause, AttrNumber *grpColIdx,
bool is_presorted, Plan *subplan);
-static Plan *make_sortplan(List *tlist, Plan *plannode, List *sortcls);
/*****************************************************************************
*
@@ -60,43 +59,32 @@ planner(Query *parse)
{
Plan *result_plan;
Index save_PlannerQueryLevel;
- List *save_PlannerInitPlan;
List *save_PlannerParamVar;
- int save_PlannerPlanId;
/*
- * The outer planner can be called recursively, for example to process
- * a subquery in the rangetable. (A less obvious example occurs when
- * eval_const_expressions tries to simplify an SQL function.)
- * So, global state variables must be saved and restored.
+ * The planner can be called recursively (an example is when
+ * eval_const_expressions tries to simplify an SQL function).
+ * So, these global state variables must be saved and restored.
*
- * (Perhaps these should be moved into the Query structure instead?)
+ * These vars cannot be moved into the Query structure since their
+ * whole purpose is communication across multiple sub-Queries.
+ *
+ * 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.
*/
save_PlannerQueryLevel = PlannerQueryLevel;
- save_PlannerInitPlan = PlannerInitPlan;
save_PlannerParamVar = PlannerParamVar;
- save_PlannerPlanId = PlannerPlanId;
- /* Initialize state for subselects */
- PlannerQueryLevel = 1;
- PlannerInitPlan = NULL;
- PlannerParamVar = NULL;
- PlannerPlanId = 0;
+ /* Initialize state for handling outer-level references and params */
+ PlannerQueryLevel = 0; /* will be 1 in top-level subquery_planner */
+ PlannerParamVar = NIL;
- /* this should go away sometime soon */
- transformKeySetQuery(parse);
-
- /* primary planning entry point (may recurse for sublinks) */
+ /* primary planning entry point (may recurse for subqueries) */
result_plan = subquery_planner(parse, -1.0 /* default case */ );
- Assert(PlannerQueryLevel == 1);
-
- /* if top-level query had subqueries, do housekeeping for them */
- if (PlannerPlanId > 0)
- {
- (void) SS_finalize_plan(result_plan);
- result_plan->initPlan = PlannerInitPlan;
- }
+ Assert(PlannerQueryLevel == 0);
/* executor wants to know total number of Params used overall */
result_plan->nParamExec = length(PlannerParamVar);
@@ -106,9 +94,7 @@ planner(Query *parse)
/* restore state for outer planner, if any */
PlannerQueryLevel = save_PlannerQueryLevel;
- PlannerInitPlan = save_PlannerInitPlan;
PlannerParamVar = save_PlannerParamVar;
- PlannerPlanId = save_PlannerPlanId;
return result_plan;
}
@@ -125,14 +111,9 @@ planner(Query *parse)
*
* Basically, this routine does the stuff that should only be done once
* per Query object. It then calls union_planner, which may be called
- * recursively on the same Query node in order to handle UNIONs and/or
- * inheritance. subquery_planner is called recursively from subselect.c
- * to handle sub-Query nodes found within the query's expressions.
- *
- * prepunion.c uses an unholy combination of calling union_planner when
- * recursing on the primary Query node, or subquery_planner when recursing
- * on a UNION'd Query node that hasn't previously been seen by
- * subquery_planner. That whole chunk of code needs rewritten from scratch.
+ * recursively on the same Query node in order to handle inheritance.
+ * subquery_planner will be called recursively to handle sub-Query nodes
+ * found within the query's expressions and rangetable.
*
* Returns a query plan.
*--------------------
@@ -140,6 +121,20 @@ planner(Query *parse)
Plan *
subquery_planner(Query *parse, double tuple_fraction)
{
+ List *saved_initplan = PlannerInitPlan;
+ int saved_planid = PlannerPlanId;
+ Plan *plan;
+ List *lst;
+
+ /* Set up for a new level of subquery */
+ PlannerQueryLevel++;
+ PlannerInitPlan = NIL;
+
+#ifdef ENABLE_KEY_SET_QUERY
+ /* this should go away sometime soon */
+ transformKeySetQuery(parse);
+#endif
+
/*
* Check to see if any subqueries in the rangetable can be merged into
* this query.
@@ -179,9 +174,9 @@ subquery_planner(Query *parse, double tuple_fraction)
EXPRKIND_HAVING);
/*
- * Do the main planning (potentially recursive)
+ * Do the main planning (potentially recursive for inheritance)
*/
- return union_planner(parse, tuple_fraction);
+ plan = union_planner(parse, tuple_fraction);
/*
* XXX should any more of union_planner's activity be moved here?
@@ -190,6 +185,35 @@ subquery_planner(Query *parse, double tuple_fraction)
* but I suspect it would pay off in simplicity and avoidance of
* wasted cycles.
*/
+
+ /*
+ * If any subplans were generated, or if we're inside a subplan,
+ * build subPlan, extParam and locParam lists for plan nodes.
+ */
+ if (PlannerPlanId != saved_planid || PlannerQueryLevel > 1)
+ {
+ (void) SS_finalize_plan(plan);
+ /*
+ * At the moment, SS_finalize_plan doesn't handle initPlans
+ * and so we assign them to the topmost plan node.
+ */
+ plan->initPlan = PlannerInitPlan;
+ /* Must add the initPlans' extParams to the topmost node's, too */
+ foreach(lst, plan->initPlan)
+ {
+ SubPlan *subplan = (SubPlan *) lfirst(lst);
+
+ plan->extParam = set_unioni(plan->extParam,
+ subplan->plan->extParam);
+ }
+ }
+
+ /* Return to outer subquery context */
+ PlannerQueryLevel--;
+ PlannerInitPlan = saved_initplan;
+ /* we do NOT restore PlannerPlanId; that's not an oversight! */
+
+ return plan;
}
/*
@@ -320,9 +344,10 @@ is_simple_subquery(Query *subquery)
if (subquery->limitOffset || subquery->limitCount)
elog(ERROR, "LIMIT is not supported in subselects");
/*
- * Can't currently pull up a union query. Maybe after querytree redesign.
+ * Can't currently pull up a query with setops.
+ * Maybe after querytree redesign...
*/
- if (subquery->unionClause)
+ if (subquery->setOperations)
return false;
/*
* Can't pull up a subquery involving grouping, aggregation, or sorting.
@@ -573,7 +598,7 @@ preprocess_qual_conditions(Query *parse, Node *jtnode)
/*--------------------
* union_planner
- * Invokes the planner on union-type queries (both regular UNIONs and
+ * Invokes the planner on union-type queries (both set operations and
* appends produced by inheritance), recursing if necessary to get them
* all, then processes normal plans.
*
@@ -606,24 +631,31 @@ union_planner(Query *parse,
Index rt_index;
List *inheritors;
- if (parse->unionClause)
+ if (parse->setOperations)
{
- result_plan = plan_union_queries(parse);
- /* XXX do we need to do this? bjm 12/19/97 */
- tlist = preprocess_targetlist(tlist,
- parse->commandType,
- parse->resultRelation,
- parse->rtable);
+ /*
+ * Construct the plan for set operations. The result will
+ * not need any work except perhaps a top-level sort.
+ */
+ result_plan = plan_set_operations(parse);
+
+ /*
+ * We should not need to call preprocess_targetlist, since we must
+ * be in a SELECT query node.
+ */
+ Assert(parse->commandType == CMD_SELECT);
/*
* We leave current_pathkeys NIL indicating we do not know sort
- * order. This is correct for the appended-together subplan
- * results, even if the subplans themselves produced sorted results.
+ * order. This is correct when the top set operation is UNION ALL,
+ * since the appended-together results are unsorted even if the
+ * subplans were sorted. For other set operations we could be
+ * smarter --- future improvement!
*/
/*
* Calculate pathkeys that represent grouping/ordering
- * requirements
+ * requirements (grouping should always be null, but...)
*/
group_pathkeys = make_pathkeys_for_sortclauses(parse->groupClause,
tlist);
@@ -886,7 +918,7 @@ union_planner(Query *parse,
tuple_fraction = 0.25;
}
- /* Generate the (sub) plan */
+ /* Generate the basic plan for this Query */
result_plan = query_planner(parse,
sub_tlist,
tuple_fraction);
@@ -1176,7 +1208,7 @@ make_groupplan(List *group_tlist,
* make_sortplan
* Add a Sort node to implement an explicit ORDER BY clause.
*/
-static Plan *
+Plan *
make_sortplan(List *tlist, Plan *plannode, List *sortcls)
{
List *sort_tlist;
diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index ad1b47aaeb6..14c9dad3ef3 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.66 2000/09/29 18:21:33 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.67 2000/10/05 19:11:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -103,9 +103,15 @@ set_plan_references(Plan *plan)
fix_expr_references(plan, (Node *) plan->qual);
break;
case T_SubqueryScan:
+ /*
+ * We do not do set_uppernode_references() here, because
+ * a SubqueryScan will always have been created with correct
+ * references to its subplan's outputs to begin with.
+ */
fix_expr_references(plan, (Node *) plan->targetlist);
fix_expr_references(plan, (Node *) plan->qual);
- /* No need to recurse into the subplan, it's fixed already */
+ /* Recurse into subplan too */
+ set_plan_references(((SubqueryScan *) plan)->subplan);
break;
case T_NestLoop:
set_join_references((Join *) plan);
@@ -132,6 +138,7 @@ set_plan_references(Plan *plan)
case T_Material:
case T_Sort:
case T_Unique:
+ case T_SetOp:
case T_Hash:
/*
@@ -170,6 +177,7 @@ set_plan_references(Plan *plan)
* Append, like Sort et al, doesn't actually evaluate its
* targetlist or quals, and we haven't bothered to give it
* its own tlist copy. So, don't fix targetlist/qual.
+ * But do recurse into subplans.
*/
foreach(pl, ((Append *) plan)->appendplans)
set_plan_references((Plan *) lfirst(pl));
diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c
index 182d1384aa1..03e38371df5 100644
--- a/src/backend/optimizer/plan/subselect.c
+++ b/src/backend/optimizer/plan/subselect.c
@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.42 2000/09/29 18:21:33 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.43 2000/10/05 19:11:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -29,7 +29,8 @@
Index PlannerQueryLevel; /* level of current query */
List *PlannerInitPlan; /* init subplans for current query */
List *PlannerParamVar; /* to get Var from Param->paramid */
-int PlannerPlanId; /* to assign unique ID to subquery plans */
+
+int PlannerPlanId = 0; /* to assign unique ID to subquery plans */
/*--------------------
* PlannerParamVar is a list of Var nodes, wherein the n'th entry
@@ -81,7 +82,7 @@ replace_var(Var *var)
/*
* If there's already a PlannerParamVar entry for this same Var, just
- * use it. NOTE: in situations involving UNION or inheritance, it is
+ * use it. NOTE: in sufficiently complex querytrees, it is
* possible for the same varno/varlevel to refer to different RTEs in
* different parts of the parsetree, so that different fields might
* end up sharing the same Param number. As long as we check the
@@ -128,11 +129,6 @@ make_subplan(SubLink *slink)
Plan *plan;
List *lst;
Node *result;
- List *saved_ip = PlannerInitPlan;
-
- PlannerInitPlan = NULL;
-
- PlannerQueryLevel++; /* we become child */
/*
* Check to see if this node was already processed; if so we have
@@ -181,45 +177,30 @@ make_subplan(SubLink *slink)
else
tuple_fraction = -1.0; /* default behavior */
- node->plan = plan = subquery_planner(subquery, tuple_fraction);
-
/*
- * Assign subPlan, extParam and locParam to plan nodes. At the moment,
- * SS_finalize_plan doesn't handle initPlan-s and so we assign them to
- * the topmost plan node and take care about its extParam too.
+ * Generate the plan for the subquery.
*/
- (void) SS_finalize_plan(plan);
- plan->initPlan = PlannerInitPlan;
-
- /* Create extParam list as union of InitPlan-s' lists */
- foreach(lst, PlannerInitPlan)
- {
- List *lp;
-
- foreach(lp, ((SubPlan *) lfirst(lst))->plan->extParam)
- {
- if (!intMember(lfirsti(lp), plan->extParam))
- plan->extParam = lappendi(plan->extParam, lfirsti(lp));
- }
- }
+ node->plan = plan = subquery_planner(subquery, tuple_fraction);
- /* and now we are parent again */
- PlannerInitPlan = saved_ip;
- PlannerQueryLevel--;
+ node->plan_id = PlannerPlanId++; /* Assign unique ID to this SubPlan */
- node->plan_id = PlannerPlanId++;
node->rtable = subquery->rtable;
node->sublink = slink;
+
slink->subselect = NULL; /* cool ?! see error check above! */
- /* make parParam list of params coming from current query level */
+ /*
+ * Make parParam list of params that current query level will pass
+ * to this child plan.
+ */
foreach(lst, plan->extParam)
{
- Var *var = nth(lfirsti(lst), PlannerParamVar);
+ int paramid = lfirsti(lst);
+ Var *var = nth(paramid, PlannerParamVar);
/* note varlevelsup is absolute level number */
if (var->varlevelsup == PlannerQueryLevel)
- node->parParam = lappendi(node->parParam, lfirsti(lst));
+ node->parParam = lappendi(node->parParam, paramid);
}
/*
@@ -625,6 +606,11 @@ SS_finalize_plan(Plan *plan)
SS_finalize_plan((Plan *) lfirst(lst)));
break;
+ case T_SubqueryScan:
+ results.paramids = set_unioni(results.paramids,
+ SS_finalize_plan(((SubqueryScan *) plan)->subplan));
+ break;
+
case T_IndexScan:
finalize_primnode((Node *) ((IndexScan *) plan)->indxqual,
&results);
@@ -667,10 +653,10 @@ SS_finalize_plan(Plan *plan)
case T_Agg:
case T_SeqScan:
- case T_SubqueryScan:
case T_Material:
case T_Sort:
case T_Unique:
+ case T_SetOp:
case T_Group:
break;
@@ -689,17 +675,18 @@ SS_finalize_plan(Plan *plan)
foreach(lst, results.paramids)
{
- Var *var = nth(lfirsti(lst), PlannerParamVar);
+ int paramid = lfirsti(lst);
+ Var *var = nth(paramid, PlannerParamVar);
/* note varlevelsup is absolute level number */
if (var->varlevelsup < PlannerQueryLevel)
- extParam = lappendi(extParam, lfirsti(lst));
+ extParam = lappendi(extParam, paramid);
else if (var->varlevelsup > PlannerQueryLevel)
elog(ERROR, "SS_finalize_plan: plan shouldn't reference subplan's variable");
else
{
Assert(var->varno == 0 && var->varattno == 0);
- locParam = lappendi(locParam, lfirsti(lst));
+ locParam = lappendi(locParam, paramid);
}
}