planner.c

来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 1,553 行 · 第 1/4 页

C
1,553
字号
	/*	 * If it's a qual or havingQual, canonicalize it, and convert it to	 * implicit-AND format.	 *	 * XXX Is there any value in re-applying eval_const_expressions after	 * canonicalize_qual?	 */	if (kind == EXPRKIND_QUAL)	{		expr = (Node *) canonicalize_qual((Expr *) expr, true);#ifdef OPTIMIZER_DEBUG		printf("After canonicalize_qual()\n");		pprint(expr);#endif	}	/* Expand SubLinks to SubPlans */	if (parse->hasSubLinks)		expr = SS_process_sublinks(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 */	if (PlannerQueryLevel > 1)		expr = SS_replace_correlation_vars(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(Query *parse, Node *jtnode){	if (jtnode == NULL)		return;	if (IsA(jtnode, RangeTblRef))	{		/* nothing to do here */	}	else if (IsA(jtnode, FromExpr))	{		FromExpr   *f = (FromExpr *) jtnode;		List	   *l;		foreach(l, f->fromlist)			preprocess_qual_conditions(parse, lfirst(l));		f->quals = preprocess_expression(parse, f->quals, EXPRKIND_QUAL);	}	else if (IsA(jtnode, JoinExpr))	{		JoinExpr   *j = (JoinExpr *) jtnode;		preprocess_qual_conditions(parse, j->larg);		preprocess_qual_conditions(parse, j->rarg);		j->quals = preprocess_expression(parse, 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.  (This is not so critical for DELETE, but for simplicity we treat * inherited DELETE the same way.)	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. * * parse is the querytree produced by the parser & rewriter. * inheritlist is an integer list of RT indexes for the result relation set. * * Returns a query plan. *-------------------- */static Plan *inheritance_planner(Query *parse, List *inheritlist){	int			parentRTindex = parse->resultRelation;	Oid			parentOID = getrelid(parentRTindex, parse->rtable);	int			mainrtlength = length(parse->rtable);	List	   *subplans = NIL;	List	   *tlist = NIL;	List	   *l;	foreach(l, inheritlist)	{		int			childRTindex = lfirsti(l);		Oid			childOID = getrelid(childRTindex, parse->rtable);		int			subrtlength;		Query	   *subquery;		Plan	   *subplan;		/* Generate modified query with this rel as target */		subquery = (Query *) adjust_inherited_attrs((Node *) parse,												parentRTindex, parentOID,												 childRTindex, childOID);		/* Generate plan */		subplan = grouping_planner(subquery, 0.0 /* retrieve all tuples */ );		subplans = lappend(subplans, subplan);		/*		 * It's possible that additional RTEs got added to the rangetable		 * due to expansion of inherited source tables (see allpaths.c).		 * If so, we must copy 'em back to the main parse tree's rtable.		 *		 * XXX my goodness this is ugly.  Really need to think about ways to		 * rein in planner's habit of scribbling on its input.		 */		subrtlength = length(subquery->rtable);		if (subrtlength > mainrtlength)		{			List	   *subrt = subquery->rtable;			while (mainrtlength-- > 0)	/* wish we had nthcdr() */				subrt = lnext(subrt);			parse->rtable = nconc(parse->rtable, subrt);			mainrtlength = subrtlength;		}		/* Save preprocessed tlist from first rel for use in Append */		if (tlist == NIL)			tlist = subplan->targetlist;	}	/* Save the target-relations list for the executor, too */	parse->resultRelations = inheritlist;	/* Mark result as unordered (probably unnecessary) */	parse->query_pathkeys = NIL;	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. * * 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 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, parse->query_pathkeys is returned as the * actual output ordering of the plan (in pathkey format). *-------------------- */static Plan *grouping_planner(Query *parse, double tuple_fraction){	List	   *tlist = parse->targetList;	Plan	   *result_plan;	List	   *current_pathkeys;	List	   *sort_pathkeys;	if (parse->setOperations)	{		/*		 * 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(parse);		/*		 * 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 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 is not allowed with UNION/INTERSECT/EXCEPT")));		/*		 * We set current_pathkeys NIL indicating we do not know sort		 * order.  This is correct when the top set operation is UNION		 * ALL, since the appended-together results are unsorted even if		 * the subplans were sorted.  For other set operations we could be		 * smarter --- room for future improvement!		 */		current_pathkeys = NIL;		/*		 * Calculate pathkeys that represent ordering requirements		 */		sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause,													  tlist);		sort_pathkeys = canonicalize_pathkeys(parse, sort_pathkeys);	}	else	{		/* No set operations, do regular planning */		List	   *sub_tlist;		List	   *group_pathkeys;		AttrNumber *groupColIdx = NULL;		bool		need_tlist_eval = true;		QualCost	tlist_cost;		double		sub_tuple_fraction;		Path	   *cheapest_path;		Path	   *sorted_path;		double		dNumGroups = 0;		long		numGroups = 0;		int			numAggs = 0;		int			numGroupCols = length(parse->groupClause);		bool		use_hashed_grouping = false;		/* Preprocess targetlist in case we are inside an INSERT/UPDATE. */		tlist = preprocess_targetlist(tlist,									  parse->commandType,									  parse->resultRelation,									  parse->rtable);		/*		 * Add TID targets for rels selected FOR UPDATE (should this be		 * done in preprocess_targetlist?).  The executor uses the TID to		 * know which rows to lock, much as for UPDATE or DELETE.		 */		if (parse->rowMarks)		{			List	   *l;			/*			 * We've got trouble if the FOR UPDATE appears inside			 * grouping, since grouping renders a reference to individual			 * tuple CTIDs invalid.  This is also checked at parse time,			 * but that's insufficient because of rule substitution, query			 * pullup, etc.			 */			CheckSelectForUpdate(parse);			/*			 * Currently the executor only supports FOR UPDATE at top			 * level			 */			if (PlannerQueryLevel > 1)				ereport(ERROR,						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),						 errmsg("SELECT FOR UPDATE is not allowed in subqueries")));			foreach(l, parse->rowMarks)			{				Index		rti = lfirsti(l);				char	   *resname;				Resdom	   *resdom;				Var		   *var;				TargetEntry *ctid;				resname = (char *) palloc(32);				snprintf(resname, 32, "ctid%u", rti);				resdom = makeResdom(length(tlist) + 1,									TIDOID,									-1,									resname,									true);				var = makeVar(rti,							  SelfItemPointerAttributeNumber,							  TIDOID,							  -1,							  0);				ctid = makeTargetEntry(resdom, (Expr *) var);				tlist = lappend(tlist, ctid);			}		}		/*		 * Generate appropriate target list for subplan; may be different		 * from tlist if grouping or aggregation is needed.		 */		sub_tlist = make_subplanTargetList(parse, tlist,										 &groupColIdx, &need_tlist_eval);		/*		 * Calculate pathkeys that represent grouping/ordering		 * requirements		 */		group_pathkeys = make_pathkeys_for_sortclauses(parse->groupClause,													   tlist);		sort_pathkeys = make_pathkeys_for_sortclauses(parse->sortClause,													  tlist);		/*		 * 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)			numAggs = count_agg_clause((Node *) tlist) +				count_agg_clause(parse->havingQual);		/*		 * 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)			parse->query_pathkeys = group_pathkeys;		else if (parse->sortClause)			parse->query_pathkeys = sort_pathkeys;		else			parse->query_pathkeys = NIL;		/*		 * Adjust tuple_fraction if we see that we are going to apply		 * limiting/grouping/aggregation/etc.  This is not overridable by		 * the caller, since it reflects plan actions that this routine		 * will certainly take, not assumptions about context.		 */		if (parse->limitCount != NULL)		{			/*			 * A LIMIT clause limits the absolute number of tuples			 * returned. However, if it's not a constant LIMIT then we			 * have to punt; for lack of a better idea, assume 10% of the			 * plan's result is wanted.			 */			double		limit_fraction = 0.0;			if (IsA(parse->limitCount, Const))			{				Const	   *limitc = (Const *) parse->limitCount;				int32		count = DatumGetInt32(limitc->constvalue);				/*				 * A NULL-constant LIMIT represents "LIMIT ALL", which we				 * treat the same as no limit (ie, expect to retrieve all				 * the tuples).				 */				if (!limitc->constisnull && count > 0)				{					limit_fraction = (double) count;					/* We must also consider the OFFSET, if present */					if (parse->limitOffset != NULL)					{						if (IsA(parse->limitOffset, Const))						{							int32		offset;							limitc = (Const *) parse->limitOffset;							offset = DatumGetInt32(limitc->constvalue);							if (!limitc->constisnull && offset > 0)								limit_fraction += (double) offset;						}

⌨️ 快捷键说明

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