joinrels.c

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

C
629
字号
/*------------------------------------------------------------------------- * * joinrels.c *	  Routines to determine which relations should be joined * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION *	  $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.63.4.2 2004/01/24 00:37:42 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "optimizer/pathnode.h"#include "optimizer/paths.h"static List *make_rels_by_clause_joins(Query *root,						  RelOptInfo *old_rel,						  List *other_rels);static List *make_rels_by_clauseless_joins(Query *root,							  RelOptInfo *old_rel,							  List *other_rels);static bool is_inside_IN(Query *root, RelOptInfo *rel);/* * make_rels_by_joins *	  Consider ways to produce join relations containing exactly 'level' *	  jointree items.  (This is one step of the dynamic-programming method *	  embodied in make_one_rel_by_joins.)  Join rel nodes for each feasible *	  combination of lower-level rels are created and returned in a list. *	  Implementation paths are created for each such joinrel, too. * * level: level of rels we want to make this time. * joinrels[j], 1 <= j < level, is a list of rels containing j items. */List *make_rels_by_joins(Query *root, int level, List **joinrels){	List	   *result_rels = NIL;	List	   *new_rels;	List	   *nr;	List	   *r;	int			k;	/*	 * First, consider left-sided and right-sided plans, in which rels of	 * exactly level-1 member relations are joined against initial	 * relations. We prefer to join using join clauses, but if we find a	 * rel of level-1 members that has no join clauses, we will generate	 * Cartesian-product joins against all initial rels not already	 * contained in it.	 *	 * In the first pass (level == 2), we try to join each initial rel to	 * each initial rel that appears later in joinrels[1].	(The	 * mirror-image joins are handled automatically by make_join_rel.)	In	 * later passes, we try to join rels of size level-1 from	 * joinrels[level-1] to each initial rel in joinrels[1].	 */	foreach(r, joinrels[level - 1])	{		RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);		List	   *other_rels;		if (level == 2)			other_rels = lnext(r);		/* only consider remaining initial										 * rels */		else			other_rels = joinrels[1];	/* consider all initial rels */		if (old_rel->joininfo != NIL)		{			/*			 * Note that if all available join clauses for this rel			 * require more than one other rel, we will fail to make any			 * joins against it here.  In most cases that's OK; it'll be			 * considered by "bushy plan" join code in a higher-level pass			 * where we have those other rels collected into a join rel.			 */			new_rels = make_rels_by_clause_joins(root,												 old_rel,												 other_rels);			/*			 * An exception occurs when there is a clauseless join inside an			 * IN (sub-SELECT) construct.  Here, the members of the subselect			 * all have join clauses (against the stuff outside the IN), but			 * they *must* be joined to each other before we can make use of			 * those join clauses.  So do the clauseless join bit.			 *			 * See also the last-ditch case below.			 */			if (new_rels == NIL && is_inside_IN(root, old_rel))				new_rels = make_rels_by_clauseless_joins(root,														 old_rel,														 other_rels);		}		else		{			/*			 * Oops, we have a relation that is not joined to any other			 * relation.  Cartesian product time.			 */			new_rels = make_rels_by_clauseless_joins(root,													 old_rel,													 other_rels);		}		/*		 * At levels above 2 we will generate the same joined relation in		 * multiple ways --- for example (a join b) join c is the same		 * RelOptInfo as (b join c) join a, though the second case will		 * add a different set of Paths to it.	To avoid making extra work		 * for subsequent passes, do not enter the same RelOptInfo into		 * our output list multiple times.		 */		foreach(nr, new_rels)		{			RelOptInfo *jrel = (RelOptInfo *) lfirst(nr);			if (!ptrMember(jrel, result_rels))				result_rels = lcons(jrel, result_rels);		}	}	/*	 * Now, consider "bushy plans" in which relations of k initial rels	 * are joined to relations of level-k initial rels, for 2 <= k <=	 * level-2.	 *	 * We only consider bushy-plan joins for pairs of rels where there is a	 * suitable join clause, in order to avoid unreasonable growth of	 * planning time.	 */	for (k = 2;; k++)	{		int			other_level = level - k;		/*		 * Since make_join_rel(x, y) handles both x,y and y,x cases, we		 * only need to go as far as the halfway point.		 */		if (k > other_level)			break;		foreach(r, joinrels[k])		{			RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);			List	   *other_rels;			List	   *r2;			if (old_rel->joininfo == NIL)				continue;		/* we ignore clauseless joins here */			if (k == other_level)				other_rels = lnext(r);	/* only consider remaining rels */			else				other_rels = joinrels[other_level];			foreach(r2, other_rels)			{				RelOptInfo *new_rel = (RelOptInfo *) lfirst(r2);				if (!bms_overlap(old_rel->relids, new_rel->relids))				{					List	   *i;					/*					 * OK, we can build a rel of the right level from this					 * pair of rels.  Do so if there is at least one					 * usable join clause.					 */					foreach(i, old_rel->joininfo)					{						JoinInfo   *joininfo = (JoinInfo *) lfirst(i);						if (bms_is_subset(joininfo->unjoined_relids,										  new_rel->relids))						{							RelOptInfo *jrel;							jrel = make_join_rel(root, old_rel, new_rel,												 JOIN_INNER);							/* Avoid making duplicate entries ... */							if (jrel && !ptrMember(jrel, result_rels))								result_rels = lcons(jrel, result_rels);							break;		/* need not consider more										 * joininfos */						}					}				}			}		}	}	/*	 * Last-ditch effort: if we failed to find any usable joins so far,	 * force a set of cartesian-product joins to be generated.	This	 * handles the special case where all the available rels have join	 * clauses but we cannot use any of the joins yet.	An example is	 *	 * SELECT * FROM a,b,c WHERE (a.f1 + b.f2 + c.f3) = 0;	 *	 * The join clause will be usable at level 3, but at level 2 we have no	 * choice but to make cartesian joins.	We consider only left-sided	 * and right-sided cartesian joins in this case (no bushy).	 */	if (result_rels == NIL)	{		/*		 * This loop is just like the first one, except we always call		 * make_rels_by_clauseless_joins().		 */		foreach(r, joinrels[level - 1])		{			RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);			List	   *other_rels;			if (level == 2)				other_rels = lnext(r);	/* only consider remaining initial										 * rels */			else				other_rels = joinrels[1];		/* consider all initial												 * rels */			new_rels = make_rels_by_clauseless_joins(root,													 old_rel,													 other_rels);			foreach(nr, new_rels)			{				RelOptInfo *jrel = (RelOptInfo *) lfirst(nr);				if (!ptrMember(jrel, result_rels))					result_rels = lcons(jrel, result_rels);			}		}		/*----------		 * When IN clauses are involved, there may be no legal way to make		 * an N-way join for some values of N.  For example consider		 *		 * SELECT ... FROM t1 WHERE		 *   x IN (SELECT ... FROM t2,t3 WHERE ...) AND		 *   y IN (SELECT ... FROM t4,t5 WHERE ...)		 *		 * We will flatten this query to a 5-way join problem, but there are		 * no 4-way joins that make_join_rel() will consider legal.  We have		 * to accept failure at level 4 and go on to discover a workable		 * bushy plan at level 5.		 *		 * However, if there are no IN clauses then make_join_rel() should		 * never fail, and so the following sanity check is useful.		 *----------		 */		if (result_rels == NIL && root->in_info_list == NIL)			elog(ERROR, "failed to build any %d-way joins", level);	}	return result_rels;}/* * make_rels_by_clause_joins *	  Build joins between the given relation 'old_rel' and other relations *	  that are mentioned within old_rel's joininfo nodes (i.e., relations *	  that participate in join clauses that 'old_rel' also participates in). *	  The join rel nodes are returned in a list. * * 'old_rel' is the relation entry for the relation to be joined * 'other_rels': other rels to be considered for joining * * Currently, this is only used with initial rels in other_rels, but it * will work for joining to joinrels too, if the caller ensures there is no * membership overlap between old_rel and the rels in other_rels.  (We need * no extra test for overlap for initial rels, since the is_subset test can * only succeed when other_rel is not already part of old_rel.) */static List *make_rels_by_clause_joins(Query *root,						  RelOptInfo *old_rel,						  List *other_rels){	List	   *result = NIL;	List	   *i,			   *j;	foreach(i, old_rel->joininfo)	{		JoinInfo   *joininfo = (JoinInfo *) lfirst(i);		Relids		unjoined_relids = joininfo->unjoined_relids;		foreach(j, other_rels)		{			RelOptInfo *other_rel = (RelOptInfo *) lfirst(j);			if (bms_is_subset(unjoined_relids, other_rel->relids))			{				RelOptInfo *jrel;				jrel = make_join_rel(root, old_rel, other_rel, JOIN_INNER);				/*				 * Avoid entering same joinrel into our output list more				 * than once.				 */				if (jrel && !ptrMember(jrel, result))					result = lcons(jrel, result);			}		}	}

⌨️ 快捷键说明

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