summaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/planner.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan/planner.c')
-rw-r--r--src/backend/optimizer/plan/planner.c529
1 files changed, 43 insertions, 486 deletions
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index cd5d266e07..388380f884 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.140 2003/01/17 03:25:03 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.141 2003/01/20 18:54:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -38,24 +38,17 @@
#include "parser/parsetree.h"
#include "parser/parse_expr.h"
#include "parser/parse_oper.h"
-#include "rewrite/rewriteManip.h"
-#include "utils/lsyscache.h"
#include "utils/selfuncs.h"
#include "utils/syscache.h"
/* Expression kind codes for preprocess_expression */
-#define EXPRKIND_TARGET 0
-#define EXPRKIND_WHERE 1
-#define EXPRKIND_HAVING 2
+#define EXPRKIND_QUAL 0
+#define EXPRKIND_TARGET 1
+#define EXPRKIND_RTFUNC 2
+#define EXPRKIND_ININFO 3
-static Node *pull_up_subqueries(Query *parse, Node *jtnode,
- bool below_outer_join);
-static bool is_simple_subquery(Query *subquery);
-static bool has_nullable_targetlist(Query *subquery);
-static void resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist);
-static Node *preprocess_jointree(Query *parse, Node *jtnode);
static Node *preprocess_expression(Query *parse, Node *expr, int kind);
static void preprocess_qual_conditions(Query *parse, Node *jtnode);
static Plan *inheritance_planner(Query *parse, List *inheritlist);
@@ -156,6 +149,17 @@ subquery_planner(Query *parse, double tuple_fraction)
PlannerInitPlan = NIL;
/*
+ * Look for IN clauses at the top level of WHERE, and transform them
+ * into joins. Note that this step only handles IN clauses originally
+ * at top level of WHERE; if we pull up any subqueries in the next step,
+ * their INs are processed just before pulling them up.
+ */
+ parse->in_info_list = NIL;
+ if (parse->hasSubLinks)
+ parse->jointree->quals = pull_up_IN_clauses(parse,
+ parse->jointree->quals);
+
+ /*
* Check to see if any subqueries in the rangetable can be merged into
* this query.
*/
@@ -195,7 +199,11 @@ subquery_planner(Query *parse, double tuple_fraction)
preprocess_qual_conditions(parse, (Node *) parse->jointree);
parse->havingQual = preprocess_expression(parse, parse->havingQual,
- EXPRKIND_HAVING);
+ EXPRKIND_QUAL);
+
+ parse->in_info_list = (List *)
+ preprocess_expression(parse, (Node *) parse->in_info_list,
+ EXPRKIND_ININFO);
/* Also need to preprocess expressions for function RTEs */
foreach(lst, parse->rtable)
@@ -204,8 +212,7 @@ subquery_planner(Query *parse, double tuple_fraction)
if (rte->rtekind == RTE_FUNCTION)
rte->funcexpr = preprocess_expression(parse, rte->funcexpr,
- EXPRKIND_TARGET);
- /* These are not targetlist items, but close enough... */
+ EXPRKIND_RTFUNC);
}
/*
@@ -296,427 +303,6 @@ subquery_planner(Query *parse, double tuple_fraction)
}
/*
- * pull_up_subqueries
- * Look for subqueries in the rangetable that can be pulled up into
- * the parent query. If the subquery has no special features like
- * grouping/aggregation then we can merge it into the parent's jointree.
- *
- * below_outer_join is true if this jointree node is within the nullable
- * side of an outer join. This restricts what we can do.
- *
- * A tricky aspect of this code is that if we pull up a subquery we have
- * to replace Vars that reference the subquery's outputs throughout the
- * parent query, including quals attached to jointree nodes above the one
- * we are currently processing! We handle this by being careful not to
- * change the jointree structure while recursing: no nodes other than
- * subquery RangeTblRef entries will be replaced. Also, we can't turn
- * ResolveNew loose on the whole jointree, because it'll return a mutated
- * copy of the tree; we have to invoke it just on the quals, instead.
- */
-static Node *
-pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join)
-{
- if (jtnode == NULL)
- return NULL;
- if (IsA(jtnode, RangeTblRef))
- {
- int varno = ((RangeTblRef *) jtnode)->rtindex;
- RangeTblEntry *rte = rt_fetch(varno, parse->rtable);
- Query *subquery = rte->subquery;
-
- /*
- * Is this a subquery RTE, and if so, is the subquery simple
- * enough to pull up? (If not, do nothing at this node.)
- *
- * If we are inside an outer join, only pull up subqueries whose
- * targetlists are nullable --- otherwise substituting their tlist
- * entries for upper Var references would do the wrong thing (the
- * results wouldn't become NULL when they're supposed to). XXX
- * This could be improved by generating pseudo-variables for such
- * expressions; we'd have to figure out how to get the pseudo-
- * variables evaluated at the right place in the modified plan
- * tree. Fix it someday.
- *
- * Note: even if the subquery itself is simple enough, we can't pull
- * it up if there is a reference to its whole tuple result.
- * Perhaps a pseudo-variable is the answer here too.
- */
- if (rte->rtekind == RTE_SUBQUERY && is_simple_subquery(subquery) &&
- (!below_outer_join || has_nullable_targetlist(subquery)) &&
- !contain_whole_tuple_var((Node *) parse, varno, 0))
- {
- int rtoffset;
- List *subtlist;
- List *rt;
-
- /*
- * First, recursively pull up the subquery's subqueries, so
- * that this routine's processing is complete for its jointree
- * and rangetable. NB: if the same subquery is referenced
- * from multiple jointree items (which can't happen normally,
- * but might after rule rewriting), then we will invoke this
- * processing multiple times on that subquery. OK because
- * nothing will happen after the first time. We do have to be
- * careful to copy everything we pull up, however, or risk
- * having chunks of structure multiply linked.
- *
- * Note: 'false' is correct here even if we are within an outer
- * join in the upper query; the lower query starts with a clean
- * slate for outer-join semantics.
- */
- subquery->jointree = (FromExpr *)
- pull_up_subqueries(subquery, (Node *) subquery->jointree,
- false);
-
- /*
- * Now make a modifiable copy of the subquery that we can run
- * OffsetVarNodes and IncrementVarSublevelsUp on.
- */
- subquery = copyObject(subquery);
-
- /*
- * Adjust level-0 varnos in subquery so that we can append its
- * rangetable to upper query's.
- */
- rtoffset = length(parse->rtable);
- OffsetVarNodes((Node *) subquery, rtoffset, 0);
-
- /*
- * Upper-level vars in subquery are now one level closer to their
- * parent than before.
- */
- IncrementVarSublevelsUp((Node *) subquery, -1, 1);
-
- /*
- * Replace all of the top query's references to the subquery's
- * outputs with copies of the adjusted subtlist items, being
- * careful not to replace any of the jointree structure.
- * (This'd be a lot cleaner if we could use
- * query_tree_mutator.)
- */
- subtlist = subquery->targetList;
- parse->targetList = (List *)
- ResolveNew((Node *) parse->targetList,
- varno, 0, subtlist, CMD_SELECT, 0);
- resolvenew_in_jointree((Node *) parse->jointree, varno, subtlist);
- Assert(parse->setOperations == NULL);
- parse->havingQual =
- ResolveNew(parse->havingQual,
- varno, 0, subtlist, CMD_SELECT, 0);
-
- foreach(rt, parse->rtable)
- {
- RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
-
- if (rte->rtekind == RTE_JOIN)
- rte->joinaliasvars = (List *)
- ResolveNew((Node *) rte->joinaliasvars,
- varno, 0, subtlist, CMD_SELECT, 0);
- }
-
- /*
- * Now append the adjusted rtable entries to upper query. (We
- * hold off until after fixing the upper rtable entries; no
- * point in running that code on the subquery ones too.)
- */
- parse->rtable = nconc(parse->rtable, subquery->rtable);
-
- /*
- * Pull up any FOR UPDATE markers, too. (OffsetVarNodes
- * already adjusted the marker values, so just nconc the
- * list.)
- */
- parse->rowMarks = nconc(parse->rowMarks, subquery->rowMarks);
-
- /*
- * Miscellaneous housekeeping.
- */
- parse->hasSubLinks |= subquery->hasSubLinks;
- /* subquery won't be pulled up if it hasAggs, so no work there */
-
- /*
- * Return the adjusted subquery jointree to replace the
- * RangeTblRef entry in my jointree.
- */
- return (Node *) subquery->jointree;
- }
- }
- else if (IsA(jtnode, FromExpr))
- {
- FromExpr *f = (FromExpr *) jtnode;
- List *l;
-
- foreach(l, f->fromlist)
- lfirst(l) = pull_up_subqueries(parse, lfirst(l),
- below_outer_join);
- }
- else if (IsA(jtnode, JoinExpr))
- {
- JoinExpr *j = (JoinExpr *) jtnode;
-
- /* Recurse, being careful to tell myself when inside outer join */
- switch (j->jointype)
- {
- case JOIN_INNER:
- j->larg = pull_up_subqueries(parse, j->larg,
- below_outer_join);
- j->rarg = pull_up_subqueries(parse, j->rarg,
- below_outer_join);
- break;
- case JOIN_LEFT:
- j->larg = pull_up_subqueries(parse, j->larg,
- below_outer_join);
- j->rarg = pull_up_subqueries(parse, j->rarg,
- true);
- break;
- case JOIN_FULL:
- j->larg = pull_up_subqueries(parse, j->larg,
- true);
- j->rarg = pull_up_subqueries(parse, j->rarg,
- true);
- break;
- case JOIN_RIGHT:
- j->larg = pull_up_subqueries(parse, j->larg,
- true);
- j->rarg = pull_up_subqueries(parse, j->rarg,
- below_outer_join);
- break;
- case JOIN_UNION:
-
- /*
- * This is where we fail if upper levels of planner
- * haven't rewritten UNION JOIN as an Append ...
- */
- elog(ERROR, "UNION JOIN is not implemented yet");
- break;
- default:
- elog(ERROR, "pull_up_subqueries: unexpected join type %d",
- j->jointype);
- break;
- }
- }
- else
- elog(ERROR, "pull_up_subqueries: unexpected node type %d",
- nodeTag(jtnode));
- return jtnode;
-}
-
-/*
- * is_simple_subquery
- * Check a subquery in the range table to see if it's simple enough
- * to pull up into the parent query.
- */
-static bool
-is_simple_subquery(Query *subquery)
-{
- /*
- * Let's just make sure it's a valid subselect ...
- */
- if (!IsA(subquery, Query) ||
- subquery->commandType != CMD_SELECT ||
- subquery->resultRelation != 0 ||
- subquery->into != NULL ||
- subquery->isPortal)
- elog(ERROR, "is_simple_subquery: subquery is bogus");
-
- /*
- * Can't currently pull up a query with setops. Maybe after querytree
- * redesign...
- */
- if (subquery->setOperations)
- return false;
-
- /*
- * Can't pull up a subquery involving grouping, aggregation, sorting,
- * or limiting.
- */
- if (subquery->hasAggs ||
- subquery->groupClause ||
- subquery->havingQual ||
- subquery->sortClause ||
- subquery->distinctClause ||
- subquery->limitOffset ||
- subquery->limitCount)
- return false;
-
- /*
- * Don't pull up a subquery that has any set-returning functions in
- * its targetlist. Otherwise we might well wind up inserting
- * set-returning functions into places where they mustn't go, such as
- * quals of higher queries.
- */
- if (expression_returns_set((Node *) subquery->targetList))
- return false;
-
- /*
- * Hack: don't try to pull up a subquery with an empty jointree.
- * query_planner() will correctly generate a Result plan for a
- * jointree that's totally empty, but I don't think the right things
- * happen if an empty FromExpr appears lower down in a jointree. Not
- * worth working hard on this, just to collapse SubqueryScan/Result
- * into Result...
- */
- if (subquery->jointree->fromlist == NIL)
- return false;
-
- return true;
-}
-
-/*
- * has_nullable_targetlist
- * Check a subquery in the range table to see if all the non-junk
- * targetlist items are simple variables (and, hence, will correctly
- * go to NULL when examined above the point of an outer join).
- *
- * A possible future extension is to accept strict functions of simple
- * variables, eg, "x + 1".
- */
-static bool
-has_nullable_targetlist(Query *subquery)
-{
- List *l;
-
- foreach(l, subquery->targetList)
- {
- TargetEntry *tle = (TargetEntry *) lfirst(l);
-
- /* ignore resjunk columns */
- if (tle->resdom->resjunk)
- continue;
-
- /* Okay if tlist item is a simple Var */
- if (tle->expr && IsA(tle->expr, Var))
- continue;
-
- return false;
- }
- return true;
-}
-
-/*
- * Helper routine for pull_up_subqueries: do ResolveNew on every expression
- * in the jointree, without changing the jointree structure itself. Ugly,
- * but there's no other way...
- */
-static void
-resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist)
-{
- if (jtnode == NULL)
- return;
- if (IsA(jtnode, RangeTblRef))
- {
- /* nothing to do here */
- }
- else if (IsA(jtnode, FromExpr))
- {
- FromExpr *f = (FromExpr *) jtnode;
- List *l;
-
- foreach(l, f->fromlist)
- resolvenew_in_jointree(lfirst(l), varno, subtlist);
- f->quals = ResolveNew(f->quals,
- varno, 0, subtlist, CMD_SELECT, 0);
- }
- else if (IsA(jtnode, JoinExpr))
- {
- JoinExpr *j = (JoinExpr *) jtnode;
-
- resolvenew_in_jointree(j->larg, varno, subtlist);
- resolvenew_in_jointree(j->rarg, varno, subtlist);
- j->quals = ResolveNew(j->quals,
- varno, 0, subtlist, CMD_SELECT, 0);
-
- /*
- * We don't bother to update the colvars list, since it won't be
- * used again ...
- */
- }
- else
- elog(ERROR, "resolvenew_in_jointree: unexpected node type %d",
- nodeTag(jtnode));
-}
-
-/*
- * preprocess_jointree
- * Attempt to simplify a query's jointree.
- *
- * If we succeed in pulling up a subquery then we might form a jointree
- * in which a FromExpr is a direct child of another FromExpr. In that
- * case we can consider collapsing the two FromExprs into one. This is
- * an optional conversion, since the planner will work correctly either
- * way. But we may find a better plan (at the cost of more planning time)
- * if we merge the two nodes.
- *
- * NOTE: don't try to do this in the same jointree scan that does subquery
- * pullup! Since we're changing the jointree structure here, that wouldn't
- * work reliably --- see comments for pull_up_subqueries().
- */
-static Node *
-preprocess_jointree(Query *parse, Node *jtnode)
-{
- if (jtnode == NULL)
- return NULL;
- if (IsA(jtnode, RangeTblRef))
- {
- /* nothing to do here... */
- }
- else if (IsA(jtnode, FromExpr))
- {
- FromExpr *f = (FromExpr *) jtnode;
- List *newlist = NIL;
- List *l;
-
- foreach(l, f->fromlist)
- {
- Node *child = (Node *) lfirst(l);
-
- /* Recursively simplify the child... */
- child = preprocess_jointree(parse, child);
- /* Now, is it a FromExpr? */
- if (child && IsA(child, FromExpr))
- {
- /*
- * Yes, so do we want to merge it into parent? Always do
- * so if child has just one element (since that doesn't
- * make the parent's list any longer). Otherwise we have
- * to be careful about the increase in planning time
- * caused by combining the two join search spaces into
- * one. Our heuristic is to merge if the merge will
- * produce a join list no longer than GEQO_RELS/2.
- * (Perhaps need an additional user parameter?)
- */
- FromExpr *subf = (FromExpr *) child;
- int childlen = length(subf->fromlist);
- int myothers = length(newlist) + length(lnext(l));
-
- if (childlen <= 1 || (childlen + myothers) <= geqo_rels / 2)
- {
- newlist = nconc(newlist, subf->fromlist);
- f->quals = make_and_qual(subf->quals, f->quals);
- }
- else
- newlist = lappend(newlist, child);
- }
- else
- newlist = lappend(newlist, child);
- }
- f->fromlist = newlist;
- }
- else if (IsA(jtnode, JoinExpr))
- {
- JoinExpr *j = (JoinExpr *) jtnode;
-
- /* Can't usefully change the JoinExpr, but recurse on children */
- j->larg = preprocess_jointree(parse, j->larg);
- j->rarg = preprocess_jointree(parse, j->rarg);
- }
- else
- elog(ERROR, "preprocess_jointree: unexpected node type %d",
- nodeTag(jtnode));
- return jtnode;
-}
-
-/*
* preprocess_expression
* Do subquery_planner's preprocessing work for an expression,
* which can be a targetlist, a WHERE clause (including JOIN/ON
@@ -731,7 +317,7 @@ preprocess_expression(Query *parse, Node *expr, int kind)
* else sublinks expanded out from join aliases wouldn't get processed.
*/
if (parse->hasJoinRTEs)
- expr = flatten_join_alias_vars(expr, parse->rtable);
+ expr = flatten_join_alias_vars(parse, expr);
/*
* Simplify constant expressions.
@@ -748,7 +334,7 @@ preprocess_expression(Query *parse, Node *expr, int kind)
* XXX Is there any value in re-applying eval_const_expressions after
* canonicalize_qual?
*/
- if (kind != EXPRKIND_TARGET)
+ if (kind == EXPRKIND_QUAL)
{
expr = (Node *) canonicalize_qual((Expr *) expr, true);
@@ -760,7 +346,7 @@ preprocess_expression(Query *parse, Node *expr, int kind)
/* Expand SubLinks to SubPlans */
if (parse->hasSubLinks)
- expr = SS_process_sublinks(expr, (kind != EXPRKIND_TARGET));
+ expr = SS_process_sublinks(expr, (kind == EXPRKIND_QUAL));
/* Replace uplevel vars with Param nodes */
if (PlannerQueryLevel > 1)
@@ -791,7 +377,7 @@ preprocess_qual_conditions(Query *parse, Node *jtnode)
foreach(l, f->fromlist)
preprocess_qual_conditions(parse, lfirst(l));
- f->quals = preprocess_expression(parse, f->quals, EXPRKIND_WHERE);
+ f->quals = preprocess_expression(parse, f->quals, EXPRKIND_QUAL);
}
else if (IsA(jtnode, JoinExpr))
{
@@ -800,7 +386,7 @@ preprocess_qual_conditions(Query *parse, Node *jtnode)
preprocess_qual_conditions(parse, j->larg);
preprocess_qual_conditions(parse, j->rarg);
- j->quals = preprocess_expression(parse, j->quals, EXPRKIND_WHERE);
+ j->quals = preprocess_expression(parse, j->quals, EXPRKIND_QUAL);
}
else
elog(ERROR, "preprocess_qual_conditions: unexpected node type %d",
@@ -1251,12 +837,16 @@ grouping_planner(Query *parse, double tuple_fraction)
*/
if (parse->groupClause)
{
+ List *groupExprs;
+
/*
* Always estimate the number of groups. We can't do this until
* after running query_planner(), either.
*/
+ groupExprs = get_sortgrouplist_exprs(parse->groupClause,
+ parse->targetList);
dNumGroups = estimate_num_groups(parse,
- parse->groupClause,
+ groupExprs,
cheapest_path->parent->rows);
/* Also want it as a long int --- but 'ware overflow! */
numGroups = (long) Min(dNumGroups, (double) LONG_MAX);
@@ -1552,8 +1142,10 @@ grouping_planner(Query *parse, double tuple_fraction)
if (parse->sortClause)
{
if (!pathkeys_contained_in(sort_pathkeys, current_pathkeys))
- result_plan = make_sortplan(parse, tlist, result_plan,
- parse->sortClause);
+ result_plan = (Plan *) make_sort_from_sortclauses(parse,
+ tlist,
+ result_plan,
+ parse->sortClause);
}
/*
@@ -1570,9 +1162,15 @@ grouping_planner(Query *parse, double tuple_fraction)
* comparable to GROUP BY.
*/
if (!parse->groupClause && !parse->hasAggs)
+ {
+ List *distinctExprs;
+
+ distinctExprs = get_sortgrouplist_exprs(parse->distinctClause,
+ parse->targetList);
result_plan->plan_rows = estimate_num_groups(parse,
- parse->distinctClause,
+ distinctExprs,
result_plan->plan_rows);
+ }
}
/*
@@ -1774,47 +1372,6 @@ make_groupsortplan(Query *parse,
}
/*
- * make_sortplan
- * Add a Sort node to implement an explicit ORDER BY clause.
- */
-Plan *
-make_sortplan(Query *parse, List *tlist, Plan *plannode, List *sortcls)
-{
- List *sort_tlist;
- List *i;
- int keyno = 0;
-
- /*
- * First make a copy of the tlist so that we don't corrupt the
- * original.
- */
- sort_tlist = new_unsorted_tlist(tlist);
-
- foreach(i, sortcls)
- {
- SortClause *sortcl = (SortClause *) lfirst(i);
- TargetEntry *tle = get_sortgroupclause_tle(sortcl, sort_tlist);
- Resdom *resdom = tle->resdom;
-
- /*
- * Check for the possibility of duplicate order-by clauses --- the
- * parser should have removed 'em, but the executor will get
- * terribly confused if any get through!
- */
- if (resdom->reskey == 0)
- {
- /* OK, insert the ordering info needed by the executor. */
- resdom->reskey = ++keyno;
- resdom->reskeyop = sortcl->sortop;
- }
- }
-
- Assert(keyno > 0);
-
- return (Plan *) make_sort(parse, sort_tlist, plannode, keyno);
-}
-
-/*
* postprocess_setop_tlist
* Fix up targetlist returned by plan_set_operations().
*