planner.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 1,820 行 · 第 1/4 页
C
1,820 行
/*------------------------------------------------------------------------- * * planner.c * The query optimizer external interface. * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.226.2.4 2008/04/17 21:22:23 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <limits.h>#include "catalog/pg_operator.h"#include "executor/executor.h"#include "executor/nodeAgg.h"#include "miscadmin.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/cost.h"#include "optimizer/pathnode.h"#include "optimizer/paths.h"#include "optimizer/planmain.h"#include "optimizer/planner.h"#include "optimizer/prep.h"#include "optimizer/subselect.h"#include "optimizer/tlist.h"#include "optimizer/var.h"#ifdef OPTIMIZER_DEBUG#include "nodes/print.h"#endif#include "parser/parse_expr.h"#include "parser/parse_oper.h"#include "parser/parsetree.h"#include "utils/lsyscache.h"#include "utils/syscache.h"/* Hook for plugins to get control in planner() */planner_hook_type planner_hook = NULL;/* Expression kind codes for preprocess_expression */#define EXPRKIND_QUAL 0#define EXPRKIND_TARGET 1#define EXPRKIND_RTFUNC 2#define EXPRKIND_VALUES 3#define EXPRKIND_LIMIT 4#define EXPRKIND_ININFO 5#define EXPRKIND_APPINFO 6static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);static void preprocess_qual_conditions(PlannerInfo *root, Node *jtnode);static Plan *inheritance_planner(PlannerInfo *root);static Plan *grouping_planner(PlannerInfo *root, double tuple_fraction);static bool is_dummy_plan(Plan *plan);static double preprocess_limit(PlannerInfo *root, double tuple_fraction, int64 *offset_est, int64 *count_est);static Oid *extract_grouping_ops(List *groupClause);static bool choose_hashed_grouping(PlannerInfo *root, double tuple_fraction, double limit_tuples, Path *cheapest_path, Path *sorted_path, Oid *groupOperators, double dNumGroups, AggClauseCounts *agg_counts);static List *make_subplanTargetList(PlannerInfo *root, List *tlist, AttrNumber **groupColIdx, bool *need_tlist_eval);static void locate_grouping_columns(PlannerInfo *root, List *tlist, List *sub_tlist, AttrNumber *groupColIdx);static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);/***************************************************************************** * * Query optimizer entry point * * To support loadable plugins that monitor or modify planner behavior, * we provide a hook variable that lets a plugin get control before and * after the standard planning process. The plugin would normally call * standard_planner(). * * Note to plugin authors: standard_planner() scribbles on its Query input, * so you'd better copy that data structure if you want to plan more than once. * *****************************************************************************/PlannedStmt *planner(Query *parse, int cursorOptions, ParamListInfo boundParams){ PlannedStmt *result; if (planner_hook) result = (*planner_hook) (parse, cursorOptions, boundParams); else result = standard_planner(parse, cursorOptions, boundParams); return result;}PlannedStmt *standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams){ PlannedStmt *result; PlannerGlobal *glob; double tuple_fraction; PlannerInfo *root; Plan *top_plan; ListCell *lp, *lr; /* Cursor options may come from caller or from DECLARE CURSOR stmt */ if (parse->utilityStmt && IsA(parse->utilityStmt, DeclareCursorStmt)) cursorOptions |= ((DeclareCursorStmt *) parse->utilityStmt)->options; /* * 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. */ glob = makeNode(PlannerGlobal); glob->boundParams = boundParams; glob->paramlist = NIL; glob->subplans = NIL; glob->subrtables = NIL; glob->rewindPlanIDs = NULL; glob->finalrtable = NIL; glob->relationOids = NIL; glob->transientPlan = false; /* Determine what fraction of the plan is likely to be scanned */ if (cursorOptions & CURSOR_OPT_FAST_PLAN) { /* * We have no real idea how many tuples the user will ultimately FETCH * from a cursor, but it seems a good bet that he doesn't want 'em * all. Optimize for 10% retrieval (you gotta better number? Should * this be a SETtable parameter?) */ tuple_fraction = 0.10; } else { /* Default assumption is we need all the tuples */ tuple_fraction = 0.0; } /* primary planning entry point (may recurse for subqueries) */ top_plan = subquery_planner(glob, parse, 1, tuple_fraction, &root); /* * If creating a plan for a scrollable cursor, make sure it can run * backwards on demand. Add a Material node at the top at need. */ if (cursorOptions & CURSOR_OPT_SCROLL) { if (!ExecSupportsBackwardScan(top_plan)) top_plan = materialize_finished_plan(top_plan); } /* final cleanup of the plan */ Assert(glob->finalrtable == NIL); top_plan = set_plan_references(glob, top_plan, root->parse->rtable); /* ... and the subplans (both regular subplans and initplans) */ Assert(list_length(glob->subplans) == list_length(glob->subrtables)); forboth(lp, glob->subplans, lr, glob->subrtables) { Plan *subplan = (Plan *) lfirst(lp); List *subrtable = (List *) lfirst(lr); lfirst(lp) = set_plan_references(glob, subplan, subrtable); } /* build the PlannedStmt result */ result = makeNode(PlannedStmt); result->commandType = parse->commandType; result->canSetTag = parse->canSetTag; result->transientPlan = glob->transientPlan; result->planTree = top_plan; result->rtable = glob->finalrtable; result->resultRelations = root->resultRelations; result->utilityStmt = parse->utilityStmt; result->intoClause = parse->intoClause; result->subplans = glob->subplans; result->rewindPlanIDs = glob->rewindPlanIDs; result->returningLists = root->returningLists; result->rowMarks = parse->rowMarks; result->relationOids = glob->relationOids; result->nParamExec = list_length(glob->paramlist); return result;}/*-------------------- * subquery_planner * 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. * * If subroot isn't NULL, we pass back the query's final PlannerInfo struct; * among other things this tells the output sort ordering of the plan. * * Basically, this routine does the stuff that should only be done once * per Query object. It then calls grouping_planner. At one time, * grouping_planner could be invoked recursively on the same Query object; * that's not currently true, but we keep the separation between the two * routines anyway, in case we need it again someday. * * subquery_planner will be called recursively to handle sub-Query nodes * found within the query's expressions and rangetable. * * Returns a query plan. *-------------------- */Plan *subquery_planner(PlannerGlobal *glob, Query *parse, Index level, double tuple_fraction, PlannerInfo **subroot){ int num_old_subplans = list_length(glob->subplans); PlannerInfo *root; Plan *plan; List *newHaving; ListCell *l; /* 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; /* * 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. */ if (parse->hasSubLinks) parse->jointree->quals = pull_up_IN_clauses(root, parse->jointree->quals); /* * Check to see if any subqueries in the rangetable can be merged into * this query. */ parse->jointree = (FromExpr *) pull_up_subqueries(root, (Node *) parse->jointree, false, false); /* * Detect whether any rangetable entries are RTE_JOIN kind; if not, we can * avoid the expense of doing flatten_join_alias_vars(). Also check for * outer joins --- if none, we can skip reduce_outer_joins() and some * other processing. This must be done after we have done * pull_up_subqueries, of course. * * Note: if reduce_outer_joins manages to eliminate all outer joins, * root->hasOuterJoins is not reset currently. This is OK since its * purpose is merely to suppress unnecessary processing in simple cases. */ root->hasJoinRTEs = false; root->hasOuterJoins = false; foreach(l, parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_JOIN) { root->hasJoinRTEs = true; if (IS_OUTER_JOIN(rte->jointype)) { root->hasOuterJoins = true; /* Can quit scanning once we find an outer join */ break; } } } /* * Expand any rangetable entries that are inheritance sets into "append * relations". This can add entries to the rangetable, but they must be * plain base relations not joins, so it's OK (and marginally more * efficient) to do it after checking for join RTEs. We must do it after * pulling up subqueries, else we'd fail to handle inherited tables in * subqueries. */ expand_inherited_tables(root); /* * Set hasHavingQual to remember if HAVING clause is present. Needed * because preprocess_expression will reduce a constant-true condition to * an empty qual list ... but "HAVING TRUE" is not a semantic no-op. */ root->hasHavingQual = (parse->havingQual != NULL); /* Clear this flag; might get set in distribute_qual_to_rels */ root->hasPseudoConstantQuals = false; /* * Do expression preprocessing on targetlist and quals. */ parse->targetList = (List *) preprocess_expression(root, (Node *) parse->targetList, EXPRKIND_TARGET); parse->returningList = (List *) preprocess_expression(root, (Node *) parse->returningList, EXPRKIND_TARGET); preprocess_qual_conditions(root, (Node *) parse->jointree); parse->havingQual = preprocess_expression(root, parse->havingQual, EXPRKIND_QUAL); parse->limitOffset = preprocess_expression(root, parse->limitOffset, EXPRKIND_LIMIT); parse->limitCount = preprocess_expression(root, parse->limitCount, EXPRKIND_LIMIT); root->in_info_list = (List *) preprocess_expression(root, (Node *) root->in_info_list, EXPRKIND_ININFO); root->append_rel_list = (List *) preprocess_expression(root, (Node *) root->append_rel_list, EXPRKIND_APPINFO); /* Also need to preprocess expressions for function and values RTEs */ foreach(l, parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); if (rte->rtekind == RTE_FUNCTION) rte->funcexpr = preprocess_expression(root, rte->funcexpr, EXPRKIND_RTFUNC); else if (rte->rtekind == RTE_VALUES) rte->values_lists = (List *) preprocess_expression(root, (Node *) rte->values_lists, EXPRKIND_VALUES); } /* * In some cases we may want to transfer a HAVING clause into WHERE. We * cannot do so if the HAVING clause contains aggregates (obviously) or * volatile functions (since a HAVING clause is supposed to be executed * only once per group). Also, it may be that the clause is so expensive * to execute that we're better off doing it only once per group, despite * the loss of selectivity. This is hard to estimate short of doing the * entire planning process twice, so we use a heuristic: clauses * containing subplans are left in HAVING. Otherwise, we move or copy the * HAVING clause into WHERE, in hopes of eliminating tuples before * aggregation instead of after. * * If the query has explicit grouping then we can simply move such a * clause into WHERE; any group that fails the clause will not be in the * output because none of its tuples will reach the grouping or * aggregation stage. Otherwise we must have a degenerate (variable-free) * HAVING clause, which we put in WHERE so that query_planner() can use it * in a gating Result node, but also keep in HAVING to ensure that we * don't emit a bogus aggregated row. (This could be done better, but it * seems not worth optimizing.) * * Note that both havingQual and parse->jointree->quals are in * implicitly-ANDed-list form at this point, even though they are declared * as Node *. */ newHaving = NIL; foreach(l, (List *) parse->havingQual) { Node *havingclause = (Node *) lfirst(l); if (contain_agg_clause(havingclause) || contain_volatile_functions(havingclause) || contain_subplans(havingclause)) { /* keep it in HAVING */ newHaving = lappend(newHaving, havingclause); } else if (parse->groupClause) { /* move it to WHERE */ parse->jointree->quals = (Node *) lappend((List *) parse->jointree->quals, havingclause); } else { /* put a copy in WHERE, keep it in HAVING */ parse->jointree->quals = (Node *) lappend((List *) parse->jointree->quals, copyObject(havingclause)); newHaving = lappend(newHaving, havingclause); } } parse->havingQual = (Node *) newHaving; /* * If we have any outer joins, try to reduce them to plain inner joins. * This step is most easily done after we've done expression * preprocessing. */ if (root->hasOuterJoins) reduce_outer_joins(root); /* * Do the main planning. If we have an inherited target relation, that * needs special processing, else go straight to grouping_planner. */ if (parse->resultRelation && rt_fetch(parse->resultRelation, parse->rtable)->inh) plan = inheritance_planner(root); else plan = grouping_planner(root, tuple_fraction); /* * If any subplans were generated, or if we're inside a subplan, build * initPlan list and extParam/allParam sets for plan nodes, and attach the * initPlans to the top plan node. */ if (list_length(glob->subplans) != num_old_subplans || root->query_level > 1) SS_finalize_plan(root, plan); /* Return internal info if caller wants it */ if (subroot) *subroot = root; return plan;}/* * preprocess_expression * Do subquery_planner's preprocessing work for an expression, * which can be a targetlist, a WHERE clause (including JOIN/ON * conditions), or a HAVING clause. */static Node *
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?