⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 planner.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 4 页
字号:
		if (use_hashed_grouping || !sorted_path)			best_path = cheapest_path;		else			best_path = sorted_path;		/*		 * Check to see if it's possible to optimize MIN/MAX aggregates. If		 * so, we will forget all the work we did so far to choose a "regular"		 * path ... but we had to do it anyway to be able to tell which way is		 * cheaper.		 */		result_plan = optimize_minmax_aggregates(root,												 tlist,												 best_path);		if (result_plan != NULL)		{			/*			 * optimize_minmax_aggregates generated the full plan, with the			 * right tlist, and it has no sort order.			 */			current_pathkeys = NIL;		}		else		{			/*			 * Normal case --- create a plan according to query_planner's			 * results.			 */			result_plan = create_plan(root, best_path);			current_pathkeys = best_path->pathkeys;			/*			 * create_plan() returns a plan with just a "flat" tlist of			 * required Vars.  Usually we need to insert the sub_tlist as the			 * tlist of the top plan node.	However, we can skip that if we			 * determined that whatever query_planner chose to return will be			 * good enough.			 */			if (need_tlist_eval)			{				/*				 * If the top-level plan node is one that cannot do expression				 * evaluation, we must insert a Result node to project the				 * desired tlist.				 */				if (!is_projection_capable_plan(result_plan))				{					result_plan = (Plan *) make_result(sub_tlist, NULL,													   result_plan);				}				else				{					/*					 * Otherwise, just replace the subplan's flat tlist with					 * the desired tlist.					 */					result_plan->targetlist = sub_tlist;				}				/*				 * Also, account for the cost of evaluation of the sub_tlist.				 *				 * Up to now, we have only been dealing with "flat" tlists,				 * containing just Vars.  So their evaluation cost is zero				 * according to the model used by cost_qual_eval() (or if you				 * prefer, the cost is factored into cpu_tuple_cost).  Thus we				 * can avoid accounting for tlist cost throughout				 * query_planner() and subroutines.  But now we've inserted a				 * tlist that might contain actual operators, sub-selects, etc				 * --- so we'd better account for its cost.				 *				 * Below this point, any tlist eval cost for added-on nodes				 * should be accounted for as we create those nodes.				 * Presently, of the node types we can add on, only Agg and				 * Group project new tlists (the rest just copy their input				 * tuples) --- so make_agg() and make_group() are responsible				 * for computing the added cost.				 */				cost_qual_eval(&tlist_cost, sub_tlist);				result_plan->startup_cost += tlist_cost.startup;				result_plan->total_cost += tlist_cost.startup +					tlist_cost.per_tuple * result_plan->plan_rows;			}			else			{				/*				 * Since we're using query_planner's tlist and not the one				 * make_subplanTargetList calculated, we have to refigure any				 * grouping-column indexes make_subplanTargetList computed.				 */				locate_grouping_columns(root, tlist, result_plan->targetlist,										groupColIdx);			}			/*			 * Insert AGG or GROUP node if needed, plus an explicit sort step			 * if necessary.			 *			 * HAVING clause, if any, becomes qual of the Agg or Group node.			 */			if (use_hashed_grouping)			{				/* Hashed aggregate plan --- no sort needed */				result_plan = (Plan *) make_agg(root,												tlist,												(List *) parse->havingQual,												AGG_HASHED,												numGroupCols,												groupColIdx,												numGroups,												agg_counts.numAggs,												result_plan);				/* Hashed aggregation produces randomly-ordered results */				current_pathkeys = NIL;			}			else if (parse->hasAggs)			{				/* Plain aggregate plan --- sort if needed */				AggStrategy aggstrategy;				if (parse->groupClause)				{					if (!pathkeys_contained_in(group_pathkeys,											   current_pathkeys))					{						result_plan = (Plan *)							make_sort_from_groupcols(root,													 parse->groupClause,													 groupColIdx,													 result_plan);						current_pathkeys = group_pathkeys;					}					aggstrategy = AGG_SORTED;					/*					 * The AGG node will not change the sort ordering of its					 * groups, so current_pathkeys describes the result too.					 */				}				else				{					aggstrategy = AGG_PLAIN;					/* Result will be only one row anyway; no sort order */					current_pathkeys = NIL;				}				result_plan = (Plan *) make_agg(root,												tlist,												(List *) parse->havingQual,												aggstrategy,												numGroupCols,												groupColIdx,												numGroups,												agg_counts.numAggs,												result_plan);			}			else if (parse->groupClause)			{				/*				 * GROUP BY without aggregation, so insert a group node (plus				 * the appropriate sort node, if necessary).				 *				 * Add an explicit sort if we couldn't make the path come out				 * the way the GROUP node needs it.				 */				if (!pathkeys_contained_in(group_pathkeys, current_pathkeys))				{					result_plan = (Plan *)						make_sort_from_groupcols(root,												 parse->groupClause,												 groupColIdx,												 result_plan);					current_pathkeys = group_pathkeys;				}				result_plan = (Plan *) make_group(root,												  tlist,												  (List *) parse->havingQual,												  numGroupCols,												  groupColIdx,												  dNumGroups,												  result_plan);				/* The Group node won't change sort ordering */			}			else if (root->hasHavingQual)			{				/*				 * No aggregates, and no GROUP BY, but we have a HAVING qual.				 * This is a degenerate case in which we are supposed to emit				 * either 0 or 1 row depending on whether HAVING succeeds.				 * Furthermore, there cannot be any variables in either HAVING				 * or the targetlist, so we actually do not need the FROM				 * table at all!  We can just throw away the plan-so-far and				 * generate a Result node.	This is a sufficiently unusual				 * corner case that it's not worth contorting the structure of				 * this routine to avoid having to generate the plan in the				 * first place.				 */				result_plan = (Plan *) make_result(tlist,												   parse->havingQual,												   NULL);			}		}						/* end of non-minmax-aggregate case */	}							/* end of if (setOperations) */	/*	 * If we were not able to make the plan come out in the right order, add	 * an explicit sort step.	 */	if (parse->sortClause)	{		if (!pathkeys_contained_in(sort_pathkeys, current_pathkeys))		{			result_plan = (Plan *)				make_sort_from_sortclauses(root,										   parse->sortClause,										   result_plan);			current_pathkeys = sort_pathkeys;		}	}	/*	 * If there is a DISTINCT clause, add the UNIQUE node.	 */	if (parse->distinctClause)	{		result_plan = (Plan *) make_unique(result_plan, parse->distinctClause);		/*		 * If there was grouping or aggregation, leave plan_rows as-is (ie,		 * assume the result was already mostly unique).  If not, use the		 * number of distinct-groups calculated by query_planner.		 */		if (!parse->groupClause && !root->hasHavingQual && !parse->hasAggs)			result_plan->plan_rows = dNumGroups;	}	/*	 * Finally, if there is a LIMIT/OFFSET clause, add the LIMIT node.	 */	if (parse->limitCount || parse->limitOffset)	{		result_plan = (Plan *) make_limit(result_plan,										  parse->limitOffset,										  parse->limitCount,										  offset_est,										  count_est);	}	/*	 * Return the actual output ordering in query_pathkeys for possible use by	 * an outer query level.	 */	root->query_pathkeys = current_pathkeys;	return result_plan;}/* * preprocess_limit - do pre-estimation for LIMIT and/or OFFSET clauses * * We try to estimate the values of the LIMIT/OFFSET clauses, and pass the * results back in *count_est and *offset_est.	These variables are set to * 0 if the corresponding clause is not present, and -1 if it's present * but we couldn't estimate the value for it.  (The "0" convention is OK * for OFFSET but a little bit bogus for LIMIT: effectively we estimate * LIMIT 0 as though it were LIMIT 1.  But this is in line with the planner's * usual practice of never estimating less than one row.)  These values will * be passed to make_limit, which see if you change this code. * * The return value is the suitably adjusted tuple_fraction to use for * planning the query.	This adjustment is not overridable, since it reflects * plan actions that grouping_planner() will certainly take, not assumptions * about context. */static doublepreprocess_limit(PlannerInfo *root, double tuple_fraction,				 int *offset_est, int *count_est){	Query	   *parse = root->parse;	Node	   *est;	double		limit_fraction;	/* Should not be called unless LIMIT or OFFSET */	Assert(parse->limitCount || parse->limitOffset);	/*	 * Try to obtain the clause values.  We use estimate_expression_value	 * primarily because it can sometimes do something useful with Params.	 */	if (parse->limitCount)	{		est = estimate_expression_value(parse->limitCount);		if (est && IsA(est, Const))		{			if (((Const *) est)->constisnull)			{				/* NULL indicates LIMIT ALL, ie, no limit */				*count_est = 0; /* treat as not present */			}			else			{				*count_est = DatumGetInt32(((Const *) est)->constvalue);				if (*count_est <= 0)					*count_est = 1;		/* force to at least 1 */			}		}		else			*count_est = -1;	/* can't estimate */	}	else		*count_est = 0;			/* not present */	if (parse->limitOffset)	{		est = estimate_expression_value(parse->limitOffset);		if (est && IsA(est, Const))		{			if (((Const *) est)->constisnull)			{				/* Treat NULL as no offset; the executor will too */				*offset_est = 0;	/* treat as not present */			}			else			{				*offset_est = DatumGetInt32(((Const *) est)->constvalue);				if (*offset_est < 0)					*offset_est = 0;	/* less than 0 is same as 0 */			}		}		else			*offset_est = -1;	/* can't estimate */	}	else		*offset_est = 0;		/* not present */	if (*count_est != 0)	{		/*		 * A LIMIT clause limits the absolute number of tuples returned.		 * However, if it's not a constant LIMIT then we have to guess; for		 * lack of a better idea, assume 10% of the plan's result is wanted.		 */		if (*count_est < 0 || *offset_est < 0)		{			/* LIMIT or OFFSET is an expression ... punt ... */			limit_fraction = 0.10;		}		else		{			/* LIMIT (plus OFFSET, if any) is max number of tuples needed */			limit_fraction = (double) *count_est + (double) *offset_est;		}		/*		 * If we have absolute limits from both caller and LIMIT, use the		 * smaller value; likewise if they are both fractional.  If one is		 * fractional and the other absolute, we can't easily determine which		 * is smaller, but we use the heuristic that the absolute will usually		 * be smaller.		 */		if (tuple_fraction >= 1.0)		{			if (limit_fraction >= 1.0)			{				/* both absolute */				tuple_fraction = Min(tuple_fraction, limit_fraction);			}			else			{				/* caller absolute, limit fractional; use caller's value */			}		}		else if (tuple_fraction > 0.0)		{			if (limit_fraction >= 1.0)			{				/* caller fractional, limit absolute; use limit */				tuple_fraction = limit_fraction;			}			else			{				/* both fractional */				tuple_fraction = Min(tuple_fraction, limit_fraction);			}		}		else		{			/* no info from caller, just use limit */			tuple_fraction = limit_fraction;		}	}	else if (*offset_est != 0 && tuple_fraction > 0.0)	{		/*		 * We have an OFFSET but no LIMIT.	This acts entirely differently		 * from the LIMIT case: here, we need to increase rather than decrease		 * the caller's tuple_fraction, because the OFFSET acts to cause more		 * tuples to be fetched instead of fewer.  This only matters if we got		 * a tuple_fraction > 0, however.		 *		 * As above, use 10% if OFFSET is present but unestimatable.		 */		if (*offset_est < 0)			limit_fraction = 0.10;		else			limit_fraction = (double) *offset_est;		/*		 * If we have absolute counts from both caller and OFFSET, add them

⌨️ 快捷键说明

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