planner.c

来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 1,820 行 · 第 1/4 页

C
1,820
字号
preprocess_expression(PlannerInfo *root, Node *expr, int kind){	/*	 * Fall out quickly if expression is empty.  This occurs often enough to	 * be worth checking.  Note that null->null is the correct conversion for	 * implicit-AND result format, too.	 */	if (expr == NULL)		return NULL;	/*	 * 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. We	 * can skip it in VALUES lists, however, since they can't contain any Vars	 * at all.	 */	if (root->hasJoinRTEs && kind != EXPRKIND_VALUES)		expr = flatten_join_alias_vars(root, expr);	/*	 * Simplify constant expressions.	 *	 * Note: this also flattens nested AND and OR expressions into N-argument	 * form.  All processing of a qual expression after this point must be	 * careful to maintain AND/OR flatness --- that is, do not generate a tree	 * with AND directly under AND, nor OR directly under OR.	 *	 * Because this is a relatively expensive process, we skip it when the	 * query is trivial, such as "SELECT 2+2;" or "INSERT ... VALUES()". The	 * expression will only be evaluated once anyway, so no point in	 * pre-simplifying; we can't execute it any faster than the executor can,	 * and we will waste cycles copying the tree.  Notice however that we	 * still must do it for quals (to get AND/OR flatness); and if we are in a	 * subquery we should not assume it will be done only once.	 *	 * For VALUES lists we never do this at all, again on the grounds that we	 * should optimize for one-time evaluation.	 */	if (kind != EXPRKIND_VALUES &&		(root->parse->jointree->fromlist != NIL ||		 kind == EXPRKIND_QUAL ||		 root->query_level > 1))		expr = eval_const_expressions(root, expr);	/*	 * If it's a qual or havingQual, canonicalize it.	 */	if (kind == EXPRKIND_QUAL)	{		expr = (Node *) canonicalize_qual((Expr *) expr);#ifdef OPTIMIZER_DEBUG		printf("After canonicalize_qual()\n");		pprint(expr);#endif	}	/* Expand SubLinks to SubPlans */	if (root->parse->hasSubLinks)		expr = SS_process_sublinks(root, expr, (kind == EXPRKIND_QUAL));	/*	 * XXX do not insert anything here unless you have grokked the comments in	 * SS_replace_correlation_vars ...	 */	/* Replace uplevel vars with Param nodes (this IS possible in VALUES) */	if (root->query_level > 1)		expr = SS_replace_correlation_vars(root, expr);	/*	 * If it's a qual or havingQual, convert it to implicit-AND format. (We	 * don't want to do this before eval_const_expressions, since the latter	 * would be unable to simplify a top-level AND correctly. Also,	 * SS_process_sublinks expects explicit-AND format.)	 */	if (kind == EXPRKIND_QUAL)		expr = (Node *) make_ands_implicit((Expr *) expr);	return expr;}/* * preprocess_qual_conditions *		Recursively scan the query's jointree and do subquery_planner's *		preprocessing work on each qual condition found therein. */static voidpreprocess_qual_conditions(PlannerInfo *root, Node *jtnode){	if (jtnode == NULL)		return;	if (IsA(jtnode, RangeTblRef))	{		/* nothing to do here */	}	else if (IsA(jtnode, FromExpr))	{		FromExpr   *f = (FromExpr *) jtnode;		ListCell   *l;		foreach(l, f->fromlist)			preprocess_qual_conditions(root, lfirst(l));		f->quals = preprocess_expression(root, f->quals, EXPRKIND_QUAL);	}	else if (IsA(jtnode, JoinExpr))	{		JoinExpr   *j = (JoinExpr *) jtnode;		preprocess_qual_conditions(root, j->larg);		preprocess_qual_conditions(root, j->rarg);		j->quals = preprocess_expression(root, j->quals, EXPRKIND_QUAL);	}	else		elog(ERROR, "unrecognized node type: %d",			 (int) nodeTag(jtnode));}/* * inheritance_planner *	  Generate a plan in the case where the result relation is an *	  inheritance set. * * We have to handle this case differently from cases where a source relation * is an inheritance set. Source inheritance is expanded at the bottom of the * plan tree (see allpaths.c), but target inheritance has to be expanded at * the top.  The reason is that for UPDATE, each target relation needs a * different targetlist matching its own column set.  Also, for both UPDATE * and DELETE, the executor needs the Append plan node at the top, else it * can't keep track of which table is the current target table.  Fortunately, * the UPDATE/DELETE target can never be the nullable side of an outer join, * so it's OK to generate the plan this way. * * Returns a query plan. */static Plan *inheritance_planner(PlannerInfo *root){	Query	   *parse = root->parse;	int			parentRTindex = parse->resultRelation;	List	   *subplans = NIL;	List	   *resultRelations = NIL;	List	   *returningLists = NIL;	List	   *rtable = NIL;	List	   *tlist = NIL;	PlannerInfo subroot;	ListCell   *l;	foreach(l, root->append_rel_list)	{		AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);		Plan	   *subplan;		/* append_rel_list contains all append rels; ignore others */		if (appinfo->parent_relid != parentRTindex)			continue;		/*		 * Generate modified query with this rel as target.  We have to be		 * prepared to translate varnos in in_info_list as well as in the		 * Query proper.		 */		memcpy(&subroot, root, sizeof(PlannerInfo));		subroot.parse = (Query *)			adjust_appendrel_attrs((Node *) parse,								   appinfo);		subroot.in_info_list = (List *)			adjust_appendrel_attrs((Node *) root->in_info_list,								   appinfo);		subroot.init_plans = NIL;		/* There shouldn't be any OJ info to translate, as yet */		Assert(subroot.oj_info_list == NIL);		/* Generate plan */		subplan = grouping_planner(&subroot, 0.0 /* retrieve all tuples */ );		/*		 * If this child rel was excluded by constraint exclusion, exclude it		 * from the plan.		 */		if (is_dummy_plan(subplan))			continue;		/* Save rtable and tlist from first rel for use below */		if (subplans == NIL)		{			rtable = subroot.parse->rtable;			tlist = subplan->targetlist;		}		subplans = lappend(subplans, subplan);		/* Make sure any initplans from this rel get into the outer list */		root->init_plans = list_concat(root->init_plans, subroot.init_plans);		/* Build target-relations list for the executor */		resultRelations = lappend_int(resultRelations, appinfo->child_relid);		/* Build list of per-relation RETURNING targetlists */		if (parse->returningList)		{			Assert(list_length(subroot.returningLists) == 1);			returningLists = list_concat(returningLists,										 subroot.returningLists);		}	}	root->resultRelations = resultRelations;	root->returningLists = returningLists;	/* Mark result as unordered (probably unnecessary) */	root->query_pathkeys = NIL;	/*	 * If we managed to exclude every child rel, return a dummy plan	 */	if (subplans == NIL)	{		root->resultRelations = list_make1_int(parentRTindex);		/* although dummy, it must have a valid tlist for executor */		tlist = preprocess_targetlist(root, parse->targetList);		return (Plan *) make_result(root,									tlist,									(Node *) list_make1(makeBoolConst(false,																	  false)),									NULL);	}	/*	 * Planning might have modified the rangetable, due to changes of the	 * Query structures inside subquery RTEs.  We have to ensure that this	 * gets propagated back to the master copy.  But can't do this until we	 * are done planning, because all the calls to grouping_planner need	 * virgin sub-Queries to work from.  (We are effectively assuming that	 * sub-Queries will get planned identically each time, or at least that	 * the impacts on their rangetables will be the same each time.)	 *	 * XXX should clean this up someday	 */	parse->rtable = rtable;	/* Suppress Append if there's only one surviving child rel */	if (list_length(subplans) == 1)		return (Plan *) linitial(subplans);	return (Plan *) make_append(subplans, true, tlist);}/*-------------------- * grouping_planner *	  Perform planning steps related to grouping, aggregation, etc. *	  This primarily means adding top-level processing to the basic *	  query plan produced by query_planner. * * tuple_fraction is the fraction of tuples we expect will be retrieved * * tuple_fraction is interpreted as follows: *	  0: expect all tuples to be retrieved (normal case) *	  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) * * Returns a query plan.  Also, root->query_pathkeys is returned as the * actual output ordering of the plan (in pathkey format). *-------------------- */static Plan *grouping_planner(PlannerInfo *root, double tuple_fraction){	Query	   *parse = root->parse;	List	   *tlist = parse->targetList;	int64		offset_est = 0;	int64		count_est = 0;	double		limit_tuples = -1.0;	Plan	   *result_plan;	List	   *current_pathkeys;	List	   *sort_pathkeys;	double		dNumGroups = 0;	/* Tweak caller-supplied tuple_fraction if have LIMIT/OFFSET */	if (parse->limitCount || parse->limitOffset)	{		tuple_fraction = preprocess_limit(root, tuple_fraction,										  &offset_est, &count_est);		/*		 * If we have a known LIMIT, and don't have an unknown OFFSET, we can		 * estimate the effects of using a bounded sort.		 */		if (count_est > 0 && offset_est >= 0)			limit_tuples = (double) count_est + (double) offset_est;	}	if (parse->setOperations)	{		List	   *set_sortclauses;		/*		 * If there's a top-level ORDER BY, assume we have to fetch all the		 * tuples.	This might seem too simplistic given all the hackery below		 * to possibly avoid the sort ... but a nonzero tuple_fraction is only		 * of use to plan_set_operations() when the setop is UNION ALL, and		 * the result of UNION ALL is always unsorted.		 */		if (parse->sortClause)			tuple_fraction = 0.0;		/*		 * Construct the plan for set operations.  The result will not need		 * any work except perhaps a top-level sort and/or LIMIT.		 */		result_plan = plan_set_operations(root, tuple_fraction,										  &set_sortclauses);		/*		 * Calculate pathkeys representing the sort order (if any) of the set		 * operation's result.  We have to do this before overwriting the sort		 * key information...		 */		current_pathkeys = make_pathkeys_for_sortclauses(root,														 set_sortclauses,													 result_plan->targetlist,														 true);		/*		 * We should not need to call preprocess_targetlist, since we must be		 * in a SELECT query node.	Instead, use the targetlist returned by		 * plan_set_operations (since this tells whether it returned any		 * resjunk columns!), and transfer any sort key information from the		 * original tlist.		 */		Assert(parse->commandType == CMD_SELECT);		tlist = postprocess_setop_tlist(result_plan->targetlist, tlist);		/*		 * Can't handle FOR UPDATE/SHARE here (parser should have checked		 * already, but let's make sure).		 */		if (parse->rowMarks)			ereport(ERROR,					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),					 errmsg("SELECT FOR UPDATE/SHARE is not allowed with UNION/INTERSECT/EXCEPT")));		/*		 * Calculate pathkeys that represent result ordering requirements		 */		sort_pathkeys = make_pathkeys_for_sortclauses(root,													  parse->sortClause,													  tlist,													  true);	}	else	{		/* No set operations, do regular planning */		List	   *sub_tlist;		List	   *group_pathkeys;		AttrNumber *groupColIdx = NULL;		Oid		   *groupOperators = NULL;		bool		need_tlist_eval = true;		QualCost	tlist_cost;		Path	   *cheapest_path;		Path	   *sorted_path;		Path	   *best_path;		long		numGroups = 0;		AggClauseCounts agg_counts;		int			numGroupCols = list_length(parse->groupClause);		bool		use_hashed_grouping = false;		MemSet(&agg_counts, 0, sizeof(AggClauseCounts));		/* Preprocess targetlist */		tlist = preprocess_targetlist(root, tlist);		/*		 * Generate appropriate target list for subplan; may be different from		 * tlist if grouping or aggregation is needed.		 */		sub_tlist = make_subplanTargetList(root, tlist,										   &groupColIdx, &need_tlist_eval);		/*		 * Calculate pathkeys that represent grouping/ordering requirements.		 * Stash them in PlannerInfo so that query_planner can canonicalize		 * them after EquivalenceClasses have been formed.		 */		root->group_pathkeys =			make_pathkeys_for_sortclauses(root,										  parse->groupClause,										  tlist,										  false);		root->sort_pathkeys =			make_pathkeys_for_sortclauses(root,										  parse->sortClause,										  tlist,										  false);		/*		 * Will need actual number of aggregates for estimating costs.		 *		 * Note: we do not attempt to detect duplicate aggregates here; a		 * somewhat-overestimated count is okay for our present purposes.		 *		 * Note: think not that we can turn off hasAggs if we find no aggs. It		 * is possible for constant-expression simplification to remove all		 * explicit references to aggs, but we still have to follow the		 * aggregate semantics (eg, producing only one output row).		 */		if (parse->hasAggs)		{			count_agg_clauses((Node *) tlist, &agg_counts);			count_agg_clauses(parse->havingQual, &agg_counts);		}		/*		 * Figure out whether we need a sorted result from query_planner.		 *		 * If we have a GROUP BY clause, then we want a result sorted properly		 * for grouping.  Otherwise, if there is an ORDER BY clause, we want		 * to sort by the ORDER BY clause.	(Note: if we have both, and ORDER		 * BY is a superset of GROUP BY, it would be tempting to request sort		 * by ORDER BY --- but that might just leave us failing to exploit an		 * available sort order at all. Needs more thought...)		 */		if (parse->groupClause)			root->query_pathkeys = root->group_pathkeys;		else if (parse->sortClause)			root->query_pathkeys = root->sort_pathkeys;		else			root->query_pathkeys = NIL;		/*		 * Generate the best unsorted and presorted paths for this Query (but		 * note there may not be any presorted path).  query_planner will also		 * estimate the number of groups in the query, and canonicalize all		 * the pathkeys.		 */		query_planner(root, sub_tlist, tuple_fraction, limit_tuples,					  &cheapest_path, &sorted_path, &dNumGroups);		group_pathkeys = root->group_pathkeys;		sort_pathkeys = root->sort_pathkeys;		/*		 * If grouping, extract the grouping operators and decide whether we		 * want to use hashed grouping.		 */		if (parse->groupClause)		{			groupOperators = extract_grouping_ops(parse->groupClause);			use_hashed_grouping =

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?