joinpath.c

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

C
999
字号
				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(PlannerInfo *root,					 RelOptInfo *joinrel,					 RelOptInfo *outerrel,					 RelOptInfo *innerrel,					 List *restrictlist,					 JoinType jointype){	bool		isouterjoin;	List	   *hashclauses;	ListCell   *l;	/*	 * 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(l, restrictlist)	{		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);		if (!restrictinfo->can_join ||			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->is_pushed_down)			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));	}}/* * best_appendrel_indexscan *	  Finds the best available set of inner indexscans for a nestloop join *	  with the given append relation on the inside and the given outer_rel *	  outside.	Returns an AppendPath comprising the best inner scans, or *	  NULL if there are no possible inner indexscans. * * Note that we currently consider only cheapest-total-cost.  It's not * very clear what cheapest-startup-cost might mean for an AppendPath. */static Path *best_appendrel_indexscan(PlannerInfo *root, RelOptInfo *rel,						 RelOptInfo *outer_rel, JoinType jointype){	int			parentRTindex = rel->relid;	List	   *append_paths = NIL;	bool		found_indexscan = false;	ListCell   *l;	foreach(l, root->append_rel_list)	{		AppendRelInfo *appinfo = (AppendRelInfo *) lfirst(l);		int			childRTindex;		RelOptInfo *childrel;		Path	   *index_cheapest_startup;		Path	   *index_cheapest_total;		/* append_rel_list contains all append rels; ignore others */		if (appinfo->parent_relid != parentRTindex)			continue;		childRTindex = appinfo->child_relid;		childrel = find_base_rel(root, childRTindex);		Assert(childrel->reloptkind == RELOPT_OTHER_MEMBER_REL);		/*		 * Check to see if child was rejected by constraint exclusion. If so,		 * it will have a cheapest_total_path that's a "dummy" path.		 */		if (IS_DUMMY_PATH(childrel->cheapest_total_path))			continue;			/* OK, we can ignore it */		/*		 * Get the best innerjoin indexpaths (if any) for this child rel.		 */		best_inner_indexscan(root, childrel, outer_rel, jointype,							 &index_cheapest_startup, &index_cheapest_total);		/*		 * If no luck on an indexpath for this rel, we'll still consider an		 * Append substituting the cheapest-total inner path.  However we must		 * find at least one indexpath, else there's not going to be any		 * improvement over the base path for the appendrel.		 */		if (index_cheapest_total)			found_indexscan = true;		else			index_cheapest_total = childrel->cheapest_total_path;		append_paths = lappend(append_paths, index_cheapest_total);	}	if (!found_indexscan)		return NULL;	/* Form and return the completed Append path. */	return (Path *) create_append_path(rel, append_paths);}/* * select_mergejoin_clauses *	  Select mergejoin clauses that are usable for a particular join. *	  Returns a list of RestrictInfo nodes for those clauses. * * We also mark each selected RestrictInfo to show which side is currently * being considered as outer.  These are transient markings that are only * good for the duration of the current add_paths_to_joinrel() call! * * 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(PlannerInfo *root,						 RelOptInfo *joinrel,						 RelOptInfo *outerrel,						 RelOptInfo *innerrel,						 List *restrictlist,						 JoinType jointype){	List	   *result_list = NIL;	bool		isouterjoin = IS_OUTER_JOIN(jointype);	bool		have_nonmergeable_joinclause = false;	ListCell   *l;	foreach(l, restrictlist)	{		RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(l);		/*		 * If processing an outer join, only use its own join clauses in the		 * merge.  For inner joins we can use pushed-down clauses too. (Note:		 * we don't set have_nonmergeable_joinclause here because pushed-down		 * clauses will become otherquals not joinquals.)		 */		if (isouterjoin && restrictinfo->is_pushed_down)			continue;		if (!restrictinfo->can_join ||			restrictinfo->mergeopfamilies == NIL)		{			have_nonmergeable_joinclause = true;			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 */			restrictinfo->outer_is_left = true;		}		else if (bms_is_subset(restrictinfo->left_relids, innerrel->relids) &&				 bms_is_subset(restrictinfo->right_relids, outerrel->relids))		{			/* lefthand side is inner */			restrictinfo->outer_is_left = false;		}		else		{			have_nonmergeable_joinclause = true;			continue;			/* no good for these input relations */		}		/*		 * Insist that each side have a non-redundant eclass.  This		 * restriction is needed because various bits of the planner expect		 * that each clause in a merge be associatable with some pathkey in a		 * canonical pathkey list, but redundant eclasses can't appear in		 * canonical sort orderings.  (XXX it might be worth relaxing this,		 * but not enough time to address it for 8.3.)		 *		 * Note: it would be bad if this condition failed for an otherwise		 * mergejoinable FULL JOIN clause, since that would result in		 * undesirable planner failure.  I believe that is not possible		 * however; a variable involved in a full join could only appear		 * in below_outer_join eclasses, which aren't considered redundant.		 *		 * This case *can* happen for left/right join clauses: the		 * outer-side variable could be equated to a constant.  Because we		 * will propagate that constant across the join clause, the loss of		 * ability to do a mergejoin is not really all that big a deal, and		 * so it's not clear that improving this is important.		 */		cache_mergeclause_eclasses(root, restrictinfo);		if (EC_MUST_BE_REDUNDANT(restrictinfo->left_ec) ||			EC_MUST_BE_REDUNDANT(restrictinfo->right_ec))		{			have_nonmergeable_joinclause = true;			continue;			/* can't handle redundant eclasses */		}		result_list = lappend(result_list, restrictinfo);	}	/*	 * 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 (have_nonmergeable_joinclause)	{		switch (jointype)		{			case JOIN_RIGHT:				return NIL;		/* not mergejoinable */			case JOIN_FULL:				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;		}	}	return result_list;}

⌨️ 快捷键说明

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