planner.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,553 行 · 第 1/4 页
C
1,553 行
/*------------------------------------------------------------------------- * * planner.c * The query optimizer external interface. * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.161.2.2 2004/05/11 02:21:55 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <limits.h>#include "catalog/pg_operator.h"#include "catalog/pg_type.h"#include "executor/executor.h"#include "miscadmin.h"#include "nodes/makefuncs.h"#ifdef OPTIMIZER_DEBUG#include "nodes/print.h"#endif#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"#include "parser/analyze.h"#include "parser/parsetree.h"#include "parser/parse_expr.h"#include "parser/parse_oper.h"#include "utils/selfuncs.h"#include "utils/syscache.h"/* Expression kind codes for preprocess_expression */#define EXPRKIND_QUAL 0#define EXPRKIND_TARGET 1#define EXPRKIND_RTFUNC 2#define EXPRKIND_LIMIT 3#define EXPRKIND_ININFO 4static 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);static Plan *grouping_planner(Query *parse, double tuple_fraction);static bool hash_safe_grouping(Query *parse);static List *make_subplanTargetList(Query *parse, List *tlist, AttrNumber **groupColIdx, bool *need_tlist_eval);static void locate_grouping_columns(Query *parse, List *tlist, List *sub_tlist, AttrNumber *groupColIdx);static List *postprocess_setop_tlist(List *new_tlist, List *orig_tlist);/***************************************************************************** * * Query optimizer entry point * *****************************************************************************/Plan *planner(Query *parse, bool isCursor, int cursorOptions){ double tuple_fraction; Plan *result_plan; Index save_PlannerQueryLevel; List *save_PlannerParamList; /* * 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. * * 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_PlannerParamList = PlannerParamList; /* Initialize state for handling outer-level references and params */ PlannerQueryLevel = 0; /* will be 1 in top-level subquery_planner */ PlannerParamList = NIL; /* Determine what fraction of the plan is likely to be scanned */ if (isCursor) { /* * 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) */ result_plan = subquery_planner(parse, tuple_fraction); Assert(PlannerQueryLevel == 0); /* * 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 (isCursor && (cursorOptions & CURSOR_OPT_SCROLL)) { if (!ExecSupportsBackwardScan(result_plan)) result_plan = materialize_finished_plan(result_plan); } /* executor wants to know total number of Params used overall */ result_plan->nParamExec = length(PlannerParamList); /* final cleanup of the plan */ set_plan_references(result_plan, parse->rtable); /* restore state for outer planner, if any */ PlannerQueryLevel = save_PlannerQueryLevel; PlannerParamList = save_PlannerParamList; return result_plan;}/*-------------------- * 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 interpreted as explained for grouping_planner, below. * * 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(Query *parse, double tuple_fraction){ List *saved_initplan = PlannerInitPlan; int saved_planid = PlannerPlanId; bool hasOuterJoins; Plan *plan; List *newHaving; List *lst; /* Set up for a new level of subquery */ PlannerQueryLevel++; 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. */ parse->jointree = (FromExpr *) pull_up_subqueries(parse, (Node *) parse->jointree, 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(). This must be done after we have done * pull_up_subqueries, of course. */ parse->hasJoinRTEs = false; hasOuterJoins = false; foreach(lst, parse->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lst); if (rte->rtekind == RTE_JOIN) { parse->hasJoinRTEs = true; if (IS_OUTER_JOIN(rte->jointype)) { hasOuterJoins = true; /* Can quit scanning once we find an outer join */ break; } } } /* * Do expression preprocessing on targetlist and quals. */ parse->targetList = (List *) preprocess_expression(parse, (Node *) parse->targetList, EXPRKIND_TARGET); preprocess_qual_conditions(parse, (Node *) parse->jointree); parse->havingQual = preprocess_expression(parse, parse->havingQual, EXPRKIND_QUAL); parse->limitOffset = preprocess_expression(parse, parse->limitOffset, EXPRKIND_LIMIT); parse->limitCount = preprocess_expression(parse, parse->limitCount, EXPRKIND_LIMIT); 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) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(lst); if (rte->rtekind == RTE_FUNCTION) rte->funcexpr = preprocess_expression(parse, rte->funcexpr, EXPRKIND_RTFUNC); } /* * A HAVING clause without aggregates is equivalent to a WHERE clause * (except it can only refer to grouped fields). Transfer any * agg-free clauses of the HAVING qual into WHERE. This may seem like * wasting cycles to cater to stupidly-written queries, but there are * other reasons for doing it. Firstly, if the query contains no aggs * at all, then we aren't going to generate an Agg plan node, and so * there'll be no place to execute HAVING conditions; without this * transfer, we'd lose the HAVING condition entirely, which is wrong. * Secondly, when we push down a qual condition into a sub-query, it's * easiest to push the qual into HAVING always, in case it contains * aggs, and then let this code sort it out. * * 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(lst, (List *) parse->havingQual) { Node *havingclause = (Node *) lfirst(lst); if (contain_agg_clause(havingclause)) newHaving = lappend(newHaving, havingclause); else parse->jointree->quals = (Node *) lappend((List *) parse->jointree->quals, 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 (hasOuterJoins) reduce_outer_joins(parse); /* * See if we can simplify the jointree; opportunities for this may * come from having pulled up subqueries, or from flattening explicit * JOIN syntax. We must do this after flattening JOIN alias * variables, since eliminating explicit JOIN nodes from the jointree * will cause get_relids_for_join() to fail. But it should happen * after reduce_outer_joins, anyway. */ parse->jointree = (FromExpr *) simplify_jointree(parse, (Node *) parse->jointree); /* * Do the main planning. If we have an inherited target relation, * that needs special processing, else go straight to * grouping_planner. */ if (parse->resultRelation && (lst = expand_inherited_rtentry(parse, parse->resultRelation, false)) != NIL) plan = inheritance_planner(parse, lst); else plan = grouping_planner(parse, tuple_fraction); /* * If any subplans were generated, or if we're inside a subplan, build * initPlan list and extParam/allParam sets for plan nodes. */ if (PlannerPlanId != saved_planid || PlannerQueryLevel > 1) { Cost initplan_cost = 0; /* Prepare extParam/allParam sets for all nodes in tree */ SS_finalize_plan(plan, parse->rtable); /* * SS_finalize_plan doesn't handle initPlans, so we have to * manually attach them to the topmost plan node, and add their * extParams to the topmost node's, too. * * We also add the total_cost of each initPlan to the startup cost of * the 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; foreach(lst, plan->initPlan) { SubPlan *initplan = (SubPlan *) lfirst(lst); plan->extParam = bms_add_members(plan->extParam, initplan->plan->extParam); /* allParam must include all members of extParam */ plan->allParam = bms_add_members(plan->allParam, plan->extParam); initplan_cost += initplan->plan->total_cost; } plan->startup_cost += initplan_cost; plan->total_cost += initplan_cost; } /* Return to outer subquery context */ PlannerQueryLevel--; PlannerInitPlan = saved_initplan; /* we do NOT restore PlannerPlanId; that's not an oversight! */ 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 *preprocess_expression(Query *parse, Node *expr, int kind){ /* * If the query has any join RTEs, replace join alias variables with * base-relation variables. We must do this before sublink processing, * else sublinks expanded out from join aliases wouldn't get * processed. */ if (parse->hasJoinRTEs) expr = flatten_join_alias_vars(parse, expr); /* * Simplify constant expressions. * * Note that at this point quals have not yet been converted to * implicit-AND form, so we can apply eval_const_expressions directly. */ expr = eval_const_expressions(expr);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?