joinpath.c

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

C
874
字号
		{			/*			 * Always consider a nestloop join with this outer and			 * cheapest-total-cost inner.  When appropriate, also consider			 * using the materialized form of the cheapest inner, the			 * cheapest-startup-cost inner path, and the best innerjoin			 * indexpath.			 */			add_path(joinrel, (Path *)					 create_nestloop_path(root,										  joinrel,										  jointype,										  outerpath,										  inner_cheapest_total,										  restrictlist,										  merge_pathkeys));			if (matpath != NULL)				add_path(joinrel, (Path *)						 create_nestloop_path(root,											  joinrel,											  jointype,											  outerpath,											  matpath,											  restrictlist,											  merge_pathkeys));			if (inner_cheapest_startup != inner_cheapest_total)				add_path(joinrel, (Path *)						 create_nestloop_path(root,											  joinrel,											  jointype,											  outerpath,											  inner_cheapest_startup,											  restrictlist,											  merge_pathkeys));			if (bestinnerjoin != NULL)				add_path(joinrel, (Path *)						 create_nestloop_path(root,											  joinrel,											  jointype,											  outerpath,											  bestinnerjoin,											  restrictlist,											  merge_pathkeys));		}		/* Can't do anything else if outer path needs to be unique'd */		if (save_jointype == JOIN_UNIQUE_OUTER)			continue;		/* Look for useful mergeclauses (if any) */		mergeclauses = find_mergeclauses_for_pathkeys(root,													  outerpath->pathkeys,													  mergeclause_list);		/*		 * Done with this outer path if no chance for a mergejoin.		 *		 * Special corner case: for "x FULL JOIN y ON true", there will be		 * no join clauses at all.  Ordinarily we'd generate a clauseless		 * nestloop path, but since mergejoin is our only join type that		 * supports FULL JOIN, it's necessary to generate a clauseless		 * mergejoin path instead.		 *		 * Unfortunately this can't easily be extended to handle the case		 * where there are joinclauses but none of them use mergejoinable		 * operators; nodeMergejoin.c can only do a full join correctly if		 * all the joinclauses are mergeclauses.		 */		if (mergeclauses == NIL)		{			if (jointype == JOIN_FULL && restrictlist == NIL)				/* okay to try for mergejoin */ ;			else				continue;		}		if (useallclauses && length(mergeclauses) != length(mergeclause_list))			continue;		/* Compute the required ordering of the inner path */		innersortkeys = make_pathkeys_for_mergeclauses(root,													   mergeclauses,													   innerrel);		/*		 * Generate a mergejoin on the basis of sorting the cheapest		 * inner. Since a sort will be needed, only cheapest total cost		 * matters.  (But create_mergejoin_path will do the right thing if		 * inner_cheapest_total is already correctly sorted.)		 */		add_path(joinrel, (Path *)				 create_mergejoin_path(root,									   joinrel,									   jointype,									   outerpath,									   inner_cheapest_total,									   restrictlist,									   merge_pathkeys,									   mergeclauses,									   NIL,									   innersortkeys));		/* Can't do anything else if inner path needs to be unique'd */		if (save_jointype == JOIN_UNIQUE_INNER)			continue;		/*		 * Look for presorted inner paths that satisfy the innersortkey		 * list --- or any truncation thereof, if we are allowed to build		 * a mergejoin using a subset of the merge clauses.  Here, we		 * consider both cheap startup cost and cheap total cost.  Ignore		 * inner_cheapest_total, since we already made a path with it.		 */		num_sortkeys = length(innersortkeys);		if (num_sortkeys > 1 && !useallclauses)			trialsortkeys = listCopy(innersortkeys);	/* need modifiable copy */		else			trialsortkeys = innersortkeys;		/* won't really truncate */		cheapest_startup_inner = NULL;		cheapest_total_inner = NULL;		for (sortkeycnt = num_sortkeys; sortkeycnt > 0; sortkeycnt--)		{			Path	   *innerpath;			List	   *newclauses = NIL;			/*			 * Look for an inner path ordered well enough for the first			 * 'sortkeycnt' innersortkeys.	NB: trialsortkeys list is			 * modified destructively, which is why we made a copy...			 */			trialsortkeys = ltruncate(sortkeycnt, trialsortkeys);			innerpath = get_cheapest_path_for_pathkeys(innerrel->pathlist,													   trialsortkeys,													   TOTAL_COST);			if (innerpath != NULL &&				innerpath != inner_cheapest_total &&				(cheapest_total_inner == NULL ||				 compare_path_costs(innerpath, cheapest_total_inner,									TOTAL_COST) < 0))			{				/* Found a cheap (or even-cheaper) sorted path */				/* Select the right mergeclauses, if we didn't already */				if (sortkeycnt < num_sortkeys)				{					newclauses =						find_mergeclauses_for_pathkeys(root,													   trialsortkeys,													   mergeclauses);					Assert(newclauses != NIL);				}				else					newclauses = mergeclauses;				add_path(joinrel, (Path *)						 create_mergejoin_path(root,											   joinrel,											   jointype,											   outerpath,											   innerpath,											   restrictlist,											   merge_pathkeys,											   newclauses,											   NIL,											   NIL));				cheapest_total_inner = innerpath;			}			/* Same on the basis of cheapest startup cost ... */			innerpath = get_cheapest_path_for_pathkeys(innerrel->pathlist,													   trialsortkeys,													   STARTUP_COST);			if (innerpath != NULL &&				innerpath != inner_cheapest_total &&				(cheapest_startup_inner == NULL ||				 compare_path_costs(innerpath, cheapest_startup_inner,									STARTUP_COST) < 0))			{				/* Found a cheap (or even-cheaper) sorted path */				if (innerpath != cheapest_total_inner)				{					/*					 * Avoid rebuilding clause list if we already made					 * one; saves memory in big join trees...					 */					if (newclauses == NIL)					{						if (sortkeycnt < num_sortkeys)						{							newclauses =								find_mergeclauses_for_pathkeys(root,														   trialsortkeys,														   mergeclauses);							Assert(newclauses != NIL);						}						else							newclauses = mergeclauses;					}					add_path(joinrel, (Path *)							 create_mergejoin_path(root,												   joinrel,												   jointype,												   outerpath,												   innerpath,												   restrictlist,												   merge_pathkeys,												   newclauses,												   NIL,												   NIL));				}				cheapest_startup_inner = innerpath;			}			/*			 * Don't consider truncated sortkeys if we need all clauses.			 */			if (useallclauses)				break;		}	}}/* * hash_inner_and_outer *	  Create hashjoin join paths by explicitly hashing both the outer and *	  inner keys of each available hash clause. * * 'joinrel' is the join relation * 'outerrel' is the outer join relation * 'innerrel' is the inner join relation * 'restrictlist' contains all of the RestrictInfo nodes for restriction *		clauses that apply to this join * 'jointype' is the type of join to do */static voidhash_inner_and_outer(Query *root,					 RelOptInfo *joinrel,					 RelOptInfo *outerrel,					 RelOptInfo *innerrel,					 List *restrictlist,					 JoinType jointype){	bool		isouterjoin;	List	   *hashclauses;	List	   *i;	/*	 * Hashjoin only supports inner, left, and IN joins.	 */	switch (jointype)	{		case JOIN_INNER:		case JOIN_IN:		case JOIN_UNIQUE_OUTER:		case JOIN_UNIQUE_INNER:			isouterjoin = false;			break;		case JOIN_LEFT:			isouterjoin = true;			break;		default:			return;	}	/*	 * We need to build only one hashpath for any given pair of outer and	 * inner relations; all of the hashable clauses will be used as keys.	 *	 * Scan the join's restrictinfo list to find hashjoinable clauses that	 * are usable with this pair of sub-relations.	 */	hashclauses = NIL;	foreach(i, restrictlist)	{		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);		if (restrictinfo->left_relids == NULL ||			restrictinfo->hashjoinoperator == InvalidOid)			continue;			/* not hashjoinable */		/*		 * If processing an outer join, only use its own join clauses for		 * hashing.  For inner joins we need not be so picky.		 */		if (isouterjoin && restrictinfo->ispusheddown)			continue;		/*		 * Check if clause is usable with these input rels.		 */		if (bms_is_subset(restrictinfo->left_relids, outerrel->relids) &&			bms_is_subset(restrictinfo->right_relids, innerrel->relids))		{			/* righthand side is inner */		}		else if (bms_is_subset(restrictinfo->left_relids, innerrel->relids) &&			 bms_is_subset(restrictinfo->right_relids, outerrel->relids))		{			/* lefthand side is inner */		}		else			continue;			/* no good for these input relations */		hashclauses = lappend(hashclauses, restrictinfo);	}	/* If we found any usable hashclauses, make a path */	if (hashclauses)	{		/*		 * We consider both the cheapest-total-cost and		 * cheapest-startup-cost outer paths.  There's no need to consider		 * any but the cheapest-total-cost inner path, however.		 */		Path	   *cheapest_startup_outer = outerrel->cheapest_startup_path;		Path	   *cheapest_total_outer = outerrel->cheapest_total_path;		Path	   *cheapest_total_inner = innerrel->cheapest_total_path;		/* Unique-ify if need be */		if (jointype == JOIN_UNIQUE_OUTER)		{			cheapest_total_outer = (Path *)				create_unique_path(root, outerrel, cheapest_total_outer);			cheapest_startup_outer = cheapest_total_outer;			jointype = JOIN_INNER;		}		else if (jointype == JOIN_UNIQUE_INNER)		{			cheapest_total_inner = (Path *)				create_unique_path(root, innerrel, cheapest_total_inner);			jointype = JOIN_INNER;		}		add_path(joinrel, (Path *)				 create_hashjoin_path(root,									  joinrel,									  jointype,									  cheapest_total_outer,									  cheapest_total_inner,									  restrictlist,									  hashclauses));		if (cheapest_startup_outer != cheapest_total_outer)			add_path(joinrel, (Path *)					 create_hashjoin_path(root,										  joinrel,										  jointype,										  cheapest_startup_outer,										  cheapest_total_inner,										  restrictlist,										  hashclauses));	}}/* * select_mergejoin_clauses *	  Select mergejoin clauses that are usable for a particular join. *	  Returns a list of RestrictInfo nodes for those clauses. * * We examine each restrictinfo clause known for the join to see * if it is mergejoinable and involves vars from the two sub-relations * currently of interest. */static List *select_mergejoin_clauses(RelOptInfo *joinrel,						 RelOptInfo *outerrel,						 RelOptInfo *innerrel,						 List *restrictlist,						 JoinType jointype){	List	   *result_list = NIL;	bool		isouterjoin = IS_OUTER_JOIN(jointype);	List	   *i;	foreach(i, restrictlist)	{		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);		/*		 * If processing an outer join, only use its own join clauses in		 * the merge.  For inner joins we need not be so picky.		 *		 * Furthermore, if it is a right/full join then *all* the explicit		 * join clauses must be mergejoinable, else the executor will		 * fail. If we are asked for a right join then just return NIL to		 * indicate no mergejoin is possible (we can handle it as a left		 * join instead). If we are asked for a full join then emit an		 * error, because there is no fallback.		 */		if (isouterjoin)		{			if (restrictinfo->ispusheddown)				continue;			switch (jointype)			{				case JOIN_RIGHT:					if (restrictinfo->left_relids == NULL ||						restrictinfo->mergejoinoperator == InvalidOid)						return NIL;		/* not mergejoinable */					break;				case JOIN_FULL:					if (restrictinfo->left_relids == NULL ||						restrictinfo->mergejoinoperator == InvalidOid)						ereport(ERROR,								(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),								 errmsg("FULL JOIN is only supported with merge-joinable join conditions")));					break;				default:					/* otherwise, it's OK to have nonmergeable join quals */					break;			}		}		if (restrictinfo->left_relids == NULL ||			restrictinfo->mergejoinoperator == InvalidOid)			continue;			/* not mergejoinable */		/*		 * Check if clause is usable with these input rels.  All the vars		 * needed on each side of the clause must be available from one or		 * the other of the input rels.		 */		if (bms_is_subset(restrictinfo->left_relids, outerrel->relids) &&			bms_is_subset(restrictinfo->right_relids, innerrel->relids))		{			/* righthand side is inner */		}		else if (bms_is_subset(restrictinfo->left_relids, innerrel->relids) &&			 bms_is_subset(restrictinfo->right_relids, outerrel->relids))		{			/* lefthand side is inner */		}		else			continue;			/* no good for these input relations */		result_list = lcons(restrictinfo, result_list);	}	return result_list;}

⌨️ 快捷键说明

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