joinpath.c

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

C
999
字号
	/*	 * Nestloop only supports inner, left, and IN joins.  Also, if we are	 * doing a right or full join, we must use *all* the mergeclauses as join	 * clauses, else we will not have a valid plan.  (Although these two flags	 * are currently inverses, keep them separate for clarity and possible	 * future changes.)	 */	switch (jointype)	{		case JOIN_INNER:		case JOIN_LEFT:		case JOIN_IN:		case JOIN_UNIQUE_OUTER:		case JOIN_UNIQUE_INNER:			nestjoinOK = true;			useallclauses = false;			break;		case JOIN_RIGHT:		case JOIN_FULL:			nestjoinOK = false;			useallclauses = true;			break;		default:			elog(ERROR, "unrecognized join type: %d",				 (int) jointype);			nestjoinOK = false; /* keep compiler quiet */			useallclauses = false;			break;	}	/*	 * If we need to unique-ify the inner path, we will consider only the	 * cheapest inner.	 */	if (jointype == JOIN_UNIQUE_INNER)	{		inner_cheapest_total = (Path *)			create_unique_path(root, innerrel, inner_cheapest_total);		inner_cheapest_startup = inner_cheapest_total;		jointype = JOIN_INNER;	}	else if (nestjoinOK)	{		/*		 * If the cheapest inner path is a join or seqscan, we should consider		 * materializing it.  (This is a heuristic: we could consider it		 * always, but for inner indexscans it's probably a waste of time.)		 */		if (!(IsA(inner_cheapest_total, IndexPath) ||			  IsA(inner_cheapest_total, BitmapHeapPath) ||			  IsA(inner_cheapest_total, TidPath)))			matpath = (Path *)				create_material_path(innerrel, inner_cheapest_total);		/*		 * Get the best innerjoin indexpaths (if any) for this outer rel.		 * They're the same for all outer paths.		 */		if (innerrel->reloptkind != RELOPT_JOINREL)		{			if (IsA(inner_cheapest_total, AppendPath))				index_cheapest_total = best_appendrel_indexscan(root,																innerrel,																outerrel,																jointype);			else if (innerrel->rtekind == RTE_RELATION)				best_inner_indexscan(root, innerrel, outerrel, jointype,									 &index_cheapest_startup,									 &index_cheapest_total);		}	}	foreach(l, outerrel->pathlist)	{		Path	   *outerpath = (Path *) lfirst(l);		List	   *merge_pathkeys;		List	   *mergeclauses;		List	   *innersortkeys;		List	   *trialsortkeys;		Path	   *cheapest_startup_inner;		Path	   *cheapest_total_inner;		int			num_sortkeys;		int			sortkeycnt;		/*		 * If we need to unique-ify the outer path, it's pointless to consider		 * any but the cheapest outer.		 */		if (save_jointype == JOIN_UNIQUE_OUTER)		{			if (outerpath != outerrel->cheapest_total_path)				continue;			outerpath = (Path *) create_unique_path(root, outerrel, outerpath);			jointype = JOIN_INNER;		}		/*		 * The result will have this sort order (even if it is implemented as		 * a nestloop, and even if some of the mergeclauses are implemented by		 * qpquals rather than as true mergeclauses):		 */		merge_pathkeys = build_join_pathkeys(root, joinrel, jointype,											 outerpath->pathkeys);		if (nestjoinOK)		{			/*			 * 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 cheapest innerjoin			 * indexpaths.			 */			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 (index_cheapest_total != NULL)				add_path(joinrel, (Path *)						 create_nestloop_path(root,											  joinrel,											  jointype,											  outerpath,											  index_cheapest_total,											  restrictlist,											  merge_pathkeys));			if (index_cheapest_startup != NULL &&				index_cheapest_startup != index_cheapest_total)				add_path(joinrel, (Path *)						 create_nestloop_path(root,											  joinrel,											  jointype,											  outerpath,											  index_cheapest_startup,											  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,													  true,													  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.		 */		if (mergeclauses == NIL)		{			if (jointype == JOIN_FULL)				 /* okay to try for mergejoin */ ;			else				continue;		}		if (useallclauses && list_length(mergeclauses) != list_length(mergeclause_list))			continue;		/* Compute the required ordering of the inner path */		innersortkeys = make_inner_pathkeys_for_merge(root,													  mergeclauses,													  outerpath->pathkeys);		/*		 * 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.  We can ignore		 * inner_cheapest_total on the first iteration, since we already made		 * a path with it --- but not on later iterations with shorter sort		 * keys, because then we are considering a different situation, viz		 * using a simpler mergejoin to avoid a sort of the inner rel.		 */		num_sortkeys = list_length(innersortkeys);		if (num_sortkeys > 1 && !useallclauses)			trialsortkeys = list_copy(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 = list_truncate(trialsortkeys, sortkeycnt);			innerpath = get_cheapest_path_for_pathkeys(innerrel->pathlist,													   trialsortkeys,													   TOTAL_COST);			if (innerpath != NULL &&				(innerpath != inner_cheapest_total ||				 sortkeycnt < num_sortkeys) &&				(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,													   false,													   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 ||				 sortkeycnt < num_sortkeys) &&				(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,															   false,															   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)

⌨️ 快捷键说明

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