indxpath.c

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

C
2,070
字号
 *		plain indexscans, so we need to segregate them from the normal case. *		Otherwise, same API as find_usable_indexes(). *		Returns a list of IndexPaths. */static List *find_saop_paths(PlannerInfo *root, RelOptInfo *rel,				List *clauses, List *outer_clauses,				bool istoplevel, RelOptInfo *outer_rel){	bool		have_saop = false;	ListCell   *l;	/*	 * Since find_usable_indexes is relatively expensive, don't bother to run	 * it unless there are some top-level ScalarArrayOpExpr clauses.	 */	foreach(l, clauses)	{		RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);		Assert(IsA(rinfo, RestrictInfo));		if (IsA(rinfo->clause, ScalarArrayOpExpr))		{			have_saop = true;			break;		}	}	if (!have_saop)		return NIL;	return find_usable_indexes(root, rel,							   clauses, outer_clauses,							   istoplevel, outer_rel,							   SAOP_REQUIRE);}/* * generate_bitmap_or_paths *		Look through the list of clauses to find OR clauses, and generate *		a BitmapOrPath for each one we can handle that way.  Return a list *		of the generated BitmapOrPaths. * * outer_clauses is a list of additional clauses that can be assumed true * for the purpose of generating indexquals, but are not to be searched for * ORs.  (See find_usable_indexes() for motivation.)  outer_rel is the outer * side when we are considering a nestloop inner indexpath. */List *generate_bitmap_or_paths(PlannerInfo *root, RelOptInfo *rel,						 List *clauses, List *outer_clauses,						 RelOptInfo *outer_rel){	List	   *result = NIL;	List	   *all_clauses;	ListCell   *l;	/*	 * We can use both the current and outer clauses as context for	 * find_usable_indexes	 */	all_clauses = list_concat(list_copy(clauses), outer_clauses);	foreach(l, clauses)	{		RestrictInfo *rinfo = (RestrictInfo *) lfirst(l);		List	   *pathlist;		Path	   *bitmapqual;		ListCell   *j;		Assert(IsA(rinfo, RestrictInfo));		/* Ignore RestrictInfos that aren't ORs */		if (!restriction_is_or_clause(rinfo))			continue;		/*		 * We must be able to match at least one index to each of the arms of		 * the OR, else we can't use it.		 */		pathlist = NIL;		foreach(j, ((BoolExpr *) rinfo->orclause)->args)		{			Node	   *orarg = (Node *) lfirst(j);			List	   *indlist;			/* OR arguments should be ANDs or sub-RestrictInfos */			if (and_clause(orarg))			{				List	   *andargs = ((BoolExpr *) orarg)->args;				indlist = find_usable_indexes(root, rel,											  andargs,											  all_clauses,											  false,											  outer_rel,											  SAOP_ALLOW);				/* Recurse in case there are sub-ORs */				indlist = list_concat(indlist,									  generate_bitmap_or_paths(root, rel,															   andargs,															   all_clauses,															   outer_rel));			}			else			{				Assert(IsA(orarg, RestrictInfo));				Assert(!restriction_is_or_clause((RestrictInfo *) orarg));				indlist = find_usable_indexes(root, rel,											  list_make1(orarg),											  all_clauses,											  false,											  outer_rel,											  SAOP_ALLOW);			}			/*			 * If nothing matched this arm, we can't do anything with this OR			 * clause.			 */			if (indlist == NIL)			{				pathlist = NIL;				break;			}			/*			 * OK, pick the most promising AND combination, and add it to			 * pathlist.			 */			bitmapqual = choose_bitmap_and(root, rel, indlist, outer_rel);			pathlist = lappend(pathlist, bitmapqual);		}		/*		 * If we have a match for every arm, then turn them into a		 * BitmapOrPath, and add to result list.		 */		if (pathlist != NIL)		{			bitmapqual = (Path *) create_bitmap_or_path(root, rel, pathlist);			result = lappend(result, bitmapqual);		}	}	return result;}/* * choose_bitmap_and *		Given a nonempty list of bitmap paths, AND them into one path. * * This is a nontrivial decision since we can legally use any subset of the * given path set.	We want to choose a good tradeoff between selectivity * and cost of computing the bitmap. * * The result is either a single one of the inputs, or a BitmapAndPath * combining multiple inputs. */static Path *choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel,				  List *paths, RelOptInfo *outer_rel){	int			npaths = list_length(paths);	PathClauseUsage **pathinfoarray;	PathClauseUsage *pathinfo;	List	   *clauselist;	List	   *bestpaths = NIL;	Cost		bestcost = 0;	int			i,				j;	ListCell   *l;	Assert(npaths > 0);			/* else caller error */	if (npaths == 1)		return (Path *) linitial(paths);		/* easy case */	/*	 * In theory we should consider every nonempty subset of the given paths.	 * In practice that seems like overkill, given the crude nature of the	 * estimates, not to mention the possible effects of higher-level AND and	 * OR clauses.	Moreover, it's completely impractical if there are a large	 * number of paths, since the work would grow as O(2^N).	 *	 * As a heuristic, we first check for paths using exactly the same sets of	 * WHERE clauses + index predicate conditions, and reject all but the	 * cheapest-to-scan in any such group.	This primarily gets rid of indexes	 * that include the interesting columns but also irrelevant columns.  (In	 * situations where the DBA has gone overboard on creating variant	 * indexes, this can make for a very large reduction in the number of	 * paths considered further.)	 *	 * We then sort the surviving paths with the cheapest-to-scan first, and	 * for each path, consider using that path alone as the basis for a bitmap	 * scan.  Then we consider bitmap AND scans formed from that path plus	 * each subsequent (higher-cost) path, adding on a subsequent path if it	 * results in a reduction in the estimated total scan cost. This means we	 * consider about O(N^2) rather than O(2^N) path combinations, which is	 * quite tolerable, especially given than N is usually reasonably small	 * because of the prefiltering step.  The cheapest of these is returned.	 *	 * We will only consider AND combinations in which no two indexes use the	 * same WHERE clause.  This is a bit of a kluge: it's needed because	 * costsize.c and clausesel.c aren't very smart about redundant clauses.	 * They will usually double-count the redundant clauses, producing a	 * too-small selectivity that makes a redundant AND step look like it	 * reduces the total cost.	Perhaps someday that code will be smarter and	 * we can remove this limitation.  (But note that this also defends	 * against flat-out duplicate input paths, which can happen because	 * best_inner_indexscan will find the same OR join clauses that	 * create_or_index_quals has pulled OR restriction clauses out of.)	 *	 * For the same reason, we reject AND combinations in which an index	 * predicate clause duplicates another clause.	Here we find it necessary	 * to be even stricter: we'll reject a partial index if any of its	 * predicate clauses are implied by the set of WHERE clauses and predicate	 * clauses used so far.  This covers cases such as a condition "x = 42"	 * used with a plain index, followed by a clauseless scan of a partial	 * index "WHERE x >= 40 AND x < 50".  The partial index has been accepted	 * only because "x = 42" was present, and so allowing it would partially	 * double-count selectivity.  (We could use predicate_implied_by on	 * regular qual clauses too, to have a more intelligent, but much more	 * expensive, check for redundancy --- but in most cases simple equality	 * seems to suffice.)	 */	/*	 * Extract clause usage info and detect any paths that use exactly the	 * same set of clauses; keep only the cheapest-to-scan of any such groups.	 * The surviving paths are put into an array for qsort'ing.	 */	pathinfoarray = (PathClauseUsage **)		palloc(npaths * sizeof(PathClauseUsage *));	clauselist = NIL;	npaths = 0;	foreach(l, paths)	{		Path	   *ipath = (Path *) lfirst(l);		pathinfo = classify_index_clause_usage(ipath, &clauselist);		for (i = 0; i < npaths; i++)		{			if (bms_equal(pathinfo->clauseids, pathinfoarray[i]->clauseids))				break;		}		if (i < npaths)		{			/* duplicate clauseids, keep the cheaper one */			Cost		ncost;			Cost		ocost;			Selectivity nselec;			Selectivity oselec;			cost_bitmap_tree_node(pathinfo->path, &ncost, &nselec);			cost_bitmap_tree_node(pathinfoarray[i]->path, &ocost, &oselec);			if (ncost < ocost)				pathinfoarray[i] = pathinfo;		}		else		{			/* not duplicate clauseids, add to array */			pathinfoarray[npaths++] = pathinfo;		}	}	/* If only one surviving path, we're done */	if (npaths == 1)		return pathinfoarray[0]->path;	/* Sort the surviving paths by index access cost */	qsort(pathinfoarray, npaths, sizeof(PathClauseUsage *),		  path_usage_comparator);	/*	 * For each surviving index, consider it as an "AND group leader", and see	 * whether adding on any of the later indexes results in an AND path with	 * cheaper total cost than before.	Then take the cheapest AND group.	 */	for (i = 0; i < npaths; i++)	{		Cost		costsofar;		List	   *qualsofar;		Bitmapset  *clauseidsofar;		ListCell   *lastcell;		pathinfo = pathinfoarray[i];		paths = list_make1(pathinfo->path);		costsofar = bitmap_scan_cost_est(root, rel, pathinfo->path, outer_rel);		qualsofar = list_concat(list_copy(pathinfo->quals),								list_copy(pathinfo->preds));		clauseidsofar = bms_copy(pathinfo->clauseids);		lastcell = list_head(paths);	/* for quick deletions */		for (j = i + 1; j < npaths; j++)		{			Cost		newcost;			pathinfo = pathinfoarray[j];			/* Check for redundancy */			if (bms_overlap(pathinfo->clauseids, clauseidsofar))				continue;		/* consider it redundant */			if (pathinfo->preds)			{				bool		redundant = false;				/* we check each predicate clause separately */				foreach(l, pathinfo->preds)				{					Node	   *np = (Node *) lfirst(l);					if (predicate_implied_by(list_make1(np), qualsofar))					{						redundant = true;						break;	/* out of inner foreach loop */					}				}				if (redundant)					continue;			}			/* tentatively add new path to paths, so we can estimate cost */			paths = lappend(paths, pathinfo->path);			newcost = bitmap_and_cost_est(root, rel, paths, outer_rel);			if (newcost < costsofar)			{				/* keep new path in paths, update subsidiary variables */				costsofar = newcost;				qualsofar = list_concat(qualsofar,										list_copy(pathinfo->quals));				qualsofar = list_concat(qualsofar,										list_copy(pathinfo->preds));				clauseidsofar = bms_add_members(clauseidsofar,												pathinfo->clauseids);				lastcell = lnext(lastcell);			}			else			{				/* reject new path, remove it from paths list */				paths = list_delete_cell(paths, lnext(lastcell), lastcell);			}			Assert(lnext(lastcell) == NULL);		}		/* Keep the cheapest AND-group (or singleton) */		if (i == 0 || costsofar < bestcost)		{			bestpaths = paths;			bestcost = costsofar;		}		/* some easy cleanup (we don't try real hard though) */		list_free(qualsofar);	}	if (list_length(bestpaths) == 1)		return (Path *) linitial(bestpaths);	/* no need for AND */	return (Path *) create_bitmap_and_path(root, rel, bestpaths);}/* qsort comparator to sort in increasing index access cost order */static intpath_usage_comparator(const void *a, const void *b){	PathClauseUsage *pa = *(PathClauseUsage *const *) a;	PathClauseUsage *pb = *(PathClauseUsage *const *) b;	Cost		acost;	Cost		bcost;	Selectivity aselec;	Selectivity bselec;	cost_bitmap_tree_node(pa->path, &acost, &aselec);	cost_bitmap_tree_node(pb->path, &bcost, &bselec);	/*	 * If costs are the same, sort by selectivity.	 */	if (acost < bcost)		return -1;	if (acost > bcost)		return 1;	if (aselec < bselec)		return -1;	if (aselec > bselec)		return 1;	return 0;}/* * Estimate the cost of actually executing a bitmap scan with a single * index path (no BitmapAnd, at least not at this level). */static Costbitmap_scan_cost_est(PlannerInfo *root, RelOptInfo *rel,					 Path *ipath, RelOptInfo *outer_rel){	Path		bpath;	cost_bitmap_heap_scan(&bpath, root, rel, ipath, outer_rel);	return bpath.total_cost;}/* * Estimate the cost of actually executing a BitmapAnd scan with the given * inputs. */static Costbitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel,					List *paths, RelOptInfo *outer_rel){	BitmapAndPath apath;	Path		bpath;

⌨️ 快捷键说明

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