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 + -
显示快捷键?