Optimizer Functions
-------------------
+The primary entry point is planner().
+
planner()
- handle inheritance by processing separately
--init_query_planner()
- preprocess target list
- preprocess qualifications(WHERE)
---query_planner()
- simplify constant subexpressions
- canonicalize_qual()
+ set up for recursive handling of subqueries
+ do final cleanup after planning.
+-subquery_planner()
+ simplify constant expressions
+ canonicalize qual
Attempt to reduce WHERE clause to either CNF or DNF canonical form.
CNF (top-level-AND) is preferred, since the optimizer can then use
any of the AND subclauses to filter tuples; but quals that are in
force them to CNF. In pathological cases either transform may expand
the qual unreasonably; so we may have to leave it un-normalized,
thereby reducing the accuracy of selectivity estimates.
+ process sublinks
+ convert Vars of outer query levels into Params
+--union_planner()
+ handle unions and inheritance by mutual recursion with prepunion.c routines
+ preprocess target list
+ handle GROUP BY, HAVING, aggregates, ORDER BY, DISTINCT
+--query_planner()
pull out constants from target list
get a target list that only contains column names, no expressions
if none, then return
* planmain.c
* Routines to plan a single query
*
+ * What's in a name, anyway? The top-level entry point of the planner/
+ * optimizer is over in planner.c, not here as you might think from the
+ * file name. But this is the main code for planning a basic join operation,
+ * shorn of features like subselects, inheritance, aggregates, grouping,
+ * and so on. (Those are the things planner.c deals with.)
+ *
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.52 2000/02/15 20:49:18 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.53 2000/03/21 05:11:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
-#include <sys/types.h>
-
#include "postgres.h"
+#include <sys/types.h>
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h"
-#include "optimizer/prep.h"
-#include "optimizer/subselect.h"
#include "optimizer/tlist.h"
-#include "utils/lsyscache.h"
static Plan *subplanner(Query *root, List *flat_tlist, List *qual,
/*--------------------
* query_planner
- * Routine to create a query plan. It does so by first creating a
- * subplan for the topmost level of attributes in the query. Then,
- * it modifies all target list and qualifications to consider the next
- * level of nesting and creates a plan for this modified query by
- * recursively calling itself. The two pieces are then merged together
- * by creating a result node that indicates which attributes should
- * be placed where and any relation level qualifications to be
- * satisfied.
+ * Generate a plan for a basic query, which may involve joins but
+ * not any fancier features.
*
- * tlist is the target list of the query (do NOT use root->targetList!)
+ * tlist is the target list the query should produce (NOT root->targetList!)
* qual is the qualification of the query (likewise!)
* tuple_fraction is the fraction of tuples we expect will be retrieved
*
+ * qual must already have been converted to implicit-AND form.
+ *
* Note: the Query node now also includes a query_pathkeys field, which
* is both an input and an output of query_planner(). The input value
* signals query_planner that the indicated sort order is wanted in the
Plan *subplan;
/*
- * Note: union_planner should already have done constant folding
- * in both the tlist and qual, so we don't do it again here
- * (indeed, we may be getting a flattened var-only tlist anyway).
- *
- * Is there any value in re-folding the qual after canonicalize_qual?
- */
-
- /*
- * Canonicalize the qual, and convert it to implicit-AND format.
- */
- qual = canonicalize_qual((Expr *) qual, true);
-#ifdef OPTIMIZER_DEBUG
- printf("After canonicalize_qual()\n");
- pprint(qual);
-#endif
-
- /* Replace uplevel vars with Param nodes */
- if (PlannerQueryLevel > 1)
- {
- tlist = (List *) SS_replace_correlation_vars((Node *) tlist);
- qual = (List *) SS_replace_correlation_vars((Node *) qual);
- }
-
- /* Expand SubLinks to SubPlans */
- if (root->hasSubLinks)
- {
- tlist = (List *) SS_process_sublinks((Node *) tlist);
- qual = (List *) SS_process_sublinks((Node *) qual);
- if (root->groupClause != NIL)
- {
- /*
- * Check for ungrouped variables passed to subplans.
- * Note we do NOT do this for subplans in WHERE; it's legal
- * there because WHERE is evaluated pre-GROUP.
- */
- check_subplans_for_ungrouped_vars((Node *) tlist, root, tlist);
- }
- }
-
- /*
* If the query contains no relation references at all, it must be
* something like "SELECT 2+2;". Build a trivial "Result" plan.
*/
}
return subplan;
-
-#ifdef NOT_USED
-
- /*
- * Destructively modify the query plan's targetlist to add fjoin lists
- * to flatten functions that return sets of base types
- */
- subplan->targetlist = generate_fjoin(subplan->targetlist);
-#endif
-
}
/*
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.77 2000/03/14 02:23:15 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.78 2000/03/21 05:12:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "utils/lsyscache.h"
#include "utils/syscache.h"
+
static List *make_subplanTargetList(Query *parse, List *tlist,
AttrNumber **groupColIdx);
static Plan *make_groupplan(List *group_tlist, bool tuplePerGroup,
PlannerParamVar = NULL;
PlannerPlanId = 0;
+ /* this should go away sometime soon */
transformKeySetQuery(parse);
- result_plan = union_planner(parse, -1.0 /* default case */);
+ /* primary planning entry point (may recurse for subplans) */
+ 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)
{
- result_plan->initPlan = PlannerInitPlan;
(void) SS_finalize_plan(result_plan);
+ result_plan->initPlan = PlannerInitPlan;
}
+
+ /* executor wants to know total number of Params used overall */
result_plan->nParamExec = length(PlannerParamVar);
+ /* final cleanup of the plan */
set_plan_references(result_plan);
return result_plan;
}
+
/*--------------------
- * union_planner
- * Invokes the planner on union-type queries (both regular UNIONs and
- * appends produced by inheritance), recursing if necessary to get them
- * all, then processes normal plans.
+ * subquery_planner
+ * Invokes the planner on a subquery. We recurse to here for each
+ * sub-SELECT found in the query tree.
*
* parse is the querytree produced by the parser & rewriter.
- * tuple_fraction is the fraction of tuples we expect will be retrieved
+ * tuple_fraction is the fraction of tuples we expect will be retrieved.
+ * tuple_fraction is interpreted as explained for union_planner, below.
*
- * tuple_fraction is interpreted as follows:
- * < 0: determine fraction by inspection of query (normal case)
- * 0: expect all tuples to be retrieved
- * 0 < tuple_fraction < 1: expect the given fraction of tuples available
- * from the plan to be retrieved
- * tuple_fraction >= 1: tuple_fraction is the absolute number of tuples
- * expected to be retrieved (ie, a LIMIT specification)
- * The normal case is to pass -1, but some callers pass values >= 0 to
- * override this routine's determination of the appropriate fraction.
+ * 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.
+ *
+ * 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.
*
* Returns a query plan.
*--------------------
*/
Plan *
-union_planner(Query *parse,
- double tuple_fraction)
+subquery_planner(Query *parse, double tuple_fraction)
{
- List *tlist = parse->targetList;
- List *rangetable = parse->rtable;
- Plan *result_plan = (Plan *) NULL;
- AttrNumber *groupColIdx = NULL;
- List *current_pathkeys = NIL;
- List *group_pathkeys;
- List *sort_pathkeys;
- Index rt_index;
-
/*
* A HAVING clause without aggregates is equivalent to a WHERE clause
* (except it can only refer to grouped fields). If there are no
* Also note that we need to do this before SS_process_sublinks,
* because that routine inserts bogus "Const" nodes.
*/
- tlist = (List *) eval_const_expressions((Node *) tlist);
+ parse->targetList = (List *)
+ eval_const_expressions((Node *) parse->targetList);
parse->qual = eval_const_expressions(parse->qual);
parse->havingQual = eval_const_expressions(parse->havingQual);
+ /*
+ * Canonicalize the qual, and convert it to implicit-AND format.
+ *
+ * XXX Is there any value in re-applying eval_const_expressions
+ * after canonicalize_qual?
+ */
+ parse->qual = (Node *) canonicalize_qual((Expr *) parse->qual, true);
+#ifdef OPTIMIZER_DEBUG
+ printf("After canonicalize_qual()\n");
+ pprint(parse->qual);
+#endif
+
+ /*
+ * Ditto for the havingQual
+ */
+ parse->havingQual = (Node *) canonicalize_qual((Expr *) parse->havingQual,
+ true);
+
+ /* Expand SubLinks to SubPlans */
+ if (parse->hasSubLinks)
+ {
+ parse->targetList = (List *)
+ SS_process_sublinks((Node *) parse->targetList);
+ parse->qual = SS_process_sublinks(parse->qual);
+ parse->havingQual = SS_process_sublinks(parse->havingQual);
+
+ if (parse->groupClause != NIL)
+ {
+ /*
+ * Check for ungrouped variables passed to subplans.
+ * Note we do NOT do this for subplans in WHERE; it's legal
+ * there because WHERE is evaluated pre-GROUP.
+ *
+ * An interesting fine point: if we reassigned a HAVING qual
+ * into WHERE above, then we will accept references to ungrouped
+ * vars from subplans in the HAVING qual. This is not entirely
+ * consistent, but it doesn't seem particularly harmful...
+ */
+ check_subplans_for_ungrouped_vars((Node *) parse->targetList,
+ parse);
+ check_subplans_for_ungrouped_vars(parse->havingQual, parse);
+ }
+ }
+
+ /* Replace uplevel vars with Param nodes */
+ if (PlannerQueryLevel > 1)
+ {
+ parse->targetList = (List *)
+ SS_replace_correlation_vars((Node *) parse->targetList);
+ parse->qual = SS_replace_correlation_vars(parse->qual);
+ parse->havingQual = SS_replace_correlation_vars(parse->havingQual);
+ }
+
+ /* Do the main planning (potentially recursive) */
+
+ return union_planner(parse, tuple_fraction);
+
+ /*
+ * XXX should any more of union_planner's activity be moved here?
+ *
+ * That would take careful study of the interactions with prepunion.c,
+ * but I suspect it would pay off in simplicity and avoidance of
+ * wasted cycles.
+ */
+}
+
+
+/*--------------------
+ * union_planner
+ * Invokes the planner on union-type queries (both regular UNIONs and
+ * appends produced by inheritance), recursing if necessary to get them
+ * all, then processes normal plans.
+ *
+ * parse is the querytree produced by the parser & rewriter.
+ * tuple_fraction is the fraction of tuples we expect will be retrieved
+ *
+ * tuple_fraction is interpreted as follows:
+ * < 0: determine fraction by inspection of query (normal case)
+ * 0: expect all tuples to be retrieved
+ * 0 < tuple_fraction < 1: expect the given fraction of tuples available
+ * from the plan to be retrieved
+ * tuple_fraction >= 1: tuple_fraction is the absolute number of tuples
+ * expected to be retrieved (ie, a LIMIT specification)
+ * The normal case is to pass -1, but some callers pass values >= 0 to
+ * override this routine's determination of the appropriate fraction.
+ *
+ * Returns a query plan.
+ *--------------------
+ */
+Plan *
+union_planner(Query *parse,
+ double tuple_fraction)
+{
+ List *tlist = parse->targetList;
+ List *rangetable = parse->rtable;
+ Plan *result_plan = (Plan *) NULL;
+ AttrNumber *groupColIdx = NULL;
+ List *current_pathkeys = NIL;
+ List *group_pathkeys;
+ List *sort_pathkeys;
+ Index rt_index;
if (parse->unionClause)
{
}
/*
- * If we have a HAVING clause, do the necessary things with it.
- * This code should parallel query_planner()'s initial processing
- * of the WHERE clause.
- */
- if (parse->havingQual)
- {
- /* Convert the havingQual to implicit-AND normal form */
- parse->havingQual = (Node *)
- canonicalize_qual((Expr *) parse->havingQual, true);
-
- /* Replace uplevel Vars with Params */
- if (PlannerQueryLevel > 1)
- parse->havingQual = SS_replace_correlation_vars(parse->havingQual);
-
- if (parse->hasSubLinks)
- {
- /* Expand SubLinks to SubPlans */
- parse->havingQual = SS_process_sublinks(parse->havingQual);
- /* Check for ungrouped variables passed to subplans */
- check_subplans_for_ungrouped_vars(parse->havingQual,
- parse,
- parse->targetList);
- }
- }
-
- /*
* If aggregate is present, insert the Agg node
*
* HAVING clause, if any, becomes qual of the Agg node
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.32 2000/03/17 02:36:15 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.33 2000/03/21 05:12:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
else
tuple_fraction = -1.0; /* default behavior */
- node->plan = plan = union_planner(subquery, tuple_fraction);
+ node->plan = plan = subquery_planner(subquery, tuple_fraction);
/*
* Assign subPlan, extParam and locParam to plan nodes. At the moment,
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.46 2000/03/14 23:06:29 thomas Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.47 2000/03/21 05:12:06 tgl Exp $
*
*-------------------------------------------------------------------------
*/
{
Query *union_query = lfirst(ulist);
+ /* use subquery_planner here because the union'd queries
+ * have not been preprocessed yet. My goodness this is messy...
+ */
union_plans = lappend(union_plans,
- union_planner(union_query, tuple_fraction));
+ subquery_planner(union_query,
+ tuple_fraction));
union_rts = lappend(union_rts, union_query->rtable);
}
}
{
Query *union_all_query = lfirst(ulist);
+ /* use subquery_planner here because the union'd queries
+ * have not been preprocessed yet. My goodness this is messy...
+ */
union_plans = lappend(union_plans,
- union_planner(union_all_query, -1.0));
+ subquery_planner(union_all_query, -1.0));
union_rts = lappend(union_rts, union_all_query->rtable);
}
}
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.62 2000/03/19 18:20:38 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.63 2000/03/21 05:12:12 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
((Node *) makeConst(BOOLOID, 1, (Datum) (val), \
(isnull), true, false, false))
-typedef struct {
- Query *query;
- List *targetList;
-} check_subplans_for_ungrouped_vars_context;
-
static bool contain_agg_clause_walker(Node *node, void *context);
static bool pull_agg_clause_walker(Node *node, List **listptr);
static bool check_subplans_for_ungrouped_vars_walker(Node *node,
- check_subplans_for_ungrouped_vars_context *context);
+ Query *context);
static int is_single_func(Node *node);
static Node *eval_const_expressions_mutator (Node *node, void *context);
static Expr *simplify_op_or_func(Expr *expr, List *args);
* In most contexts, ungrouped variables will be detected by the parser (see
* parse_agg.c, check_ungrouped_columns()). But that routine currently does
* not check subplans, because the necessary info is not computed until the
- * planner runs. So we do it here, after we have processed the subplan.
- * This ought to be cleaned up someday.
+ * planner runs. So we do it here, after we have processed sublinks into
+ * subplans. This ought to be cleaned up someday.
*
* 'clause' is the expression tree to be searched for subplans.
- * 'query' provides the GROUP BY list and range table.
- * 'targetList' is the target list that the group clauses refer to.
- * (Is it really necessary to pass the tlist separately? Couldn't we
- * just use the tlist found in the query node?)
+ * 'query' provides the GROUP BY list, the target list that the group clauses
+ * refer to, and the range table.
*/
void
check_subplans_for_ungrouped_vars(Node *clause,
- Query *query,
- List *targetList)
+ Query *query)
{
- check_subplans_for_ungrouped_vars_context context;
-
- context.query = query;
- context.targetList = targetList;
- check_subplans_for_ungrouped_vars_walker(clause, &context);
+ /* No special setup needed; context for walker is just the Query pointer */
+ check_subplans_for_ungrouped_vars_walker(clause, query);
}
static bool
check_subplans_for_ungrouped_vars_walker(Node *node,
- check_subplans_for_ungrouped_vars_context *context)
+ Query *context)
{
if (node == NULL)
return false;
* Else, see if it is a grouping column.
*/
contained_in_group_clause = false;
- foreach(gl, context->query->groupClause)
+ foreach(gl, context->groupClause)
{
GroupClause *gcl = lfirst(gl);
Node *groupexpr;
char *attname;
Assert(var->varno > 0 &&
- var->varno <= length(context->query->rtable));
- rte = rt_fetch(var->varno, context->query->rtable);
+ var->varno <= length(context->rtable));
+ rte = rt_fetch(var->varno, context->rtable);
attname = get_attname(rte->relid, var->varattno);
if (! attname)
elog(ERROR, "cache lookup of attribute %d in relation %u failed",
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: clauses.h,v 1.33 2000/01/26 05:58:20 momjian Exp $
+ * $Id: clauses.h,v 1.34 2000/03/21 05:11:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
extern List *pull_constant_clauses(List *quals, List **constantQual);
extern bool contain_agg_clause(Node *clause);
extern List *pull_agg_clause(Node *clause);
-extern void check_subplans_for_ungrouped_vars(Node *clause,
- Query *query,
- List *targetList);
+extern void check_subplans_for_ungrouped_vars(Node *clause, Query *query);
extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars);
extern int NumRelids(Node *clause);
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $Id: planner.h,v 1.14 2000/02/15 20:49:26 tgl Exp $
+ * $Id: planner.h,v 1.15 2000/03/21 05:11:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "nodes/plannodes.h"
extern Plan *planner(Query *parse);
+extern Plan *subquery_planner(Query *parse, double tuple_fraction);
extern Plan *union_planner(Query *parse, double tuple_fraction);
extern void pg_checkretval(Oid rettype, List *querytree_list);