mirror of
				https://github.com/postgres/postgres.git
				synced 2025-11-03 09:13:20 +03:00 
			
		
		
		
	Revert my best_inner_indexscan patch of yesterday, which turns out to have
had a bad side-effect: it stopped finding plans that involved BitmapAnd combinations of indexscans using both join and non-join conditions. Instead, make choose_bitmap_and more aggressive about detecting redundancies between BitmapOr subplans.
This commit is contained in:
		@@ -9,7 +9,7 @@
 | 
				
			|||||||
 *
 | 
					 *
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * IDENTIFICATION
 | 
					 * IDENTIFICATION
 | 
				
			||||||
 *	  $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.191.2.6 2006/04/08 21:32:25 tgl Exp $
 | 
					 *	  $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.191.2.7 2006/04/09 18:18:59 tgl Exp $
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 *-------------------------------------------------------------------------
 | 
					 *-------------------------------------------------------------------------
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -54,6 +54,7 @@ static List *find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
 | 
				
			|||||||
static Path *choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths);
 | 
					static Path *choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths);
 | 
				
			||||||
static int	bitmap_path_comparator(const void *a, const void *b);
 | 
					static int	bitmap_path_comparator(const void *a, const void *b);
 | 
				
			||||||
static Cost bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths);
 | 
					static Cost bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths);
 | 
				
			||||||
 | 
					static List *pull_indexpath_quals(Path *bitmapqual);
 | 
				
			||||||
static bool lists_intersect_ptr(List *list1, List *list2);
 | 
					static bool lists_intersect_ptr(List *list1, List *list2);
 | 
				
			||||||
static bool match_clause_to_indexcol(IndexOptInfo *index,
 | 
					static bool match_clause_to_indexcol(IndexOptInfo *index,
 | 
				
			||||||
						 int indexcol, Oid opclass,
 | 
											 int indexcol, Oid opclass,
 | 
				
			||||||
@@ -243,10 +244,6 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
 | 
				
			|||||||
	List	   *all_clauses = NIL;		/* not computed till needed */
 | 
						List	   *all_clauses = NIL;		/* not computed till needed */
 | 
				
			||||||
	ListCell   *ilist;
 | 
						ListCell   *ilist;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* quick exit if no available clauses */
 | 
					 | 
				
			||||||
	if (clauses == NIL)
 | 
					 | 
				
			||||||
		return NIL;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	foreach(ilist, rel->indexlist)
 | 
						foreach(ilist, rel->indexlist)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
 | 
							IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
 | 
				
			||||||
@@ -531,8 +528,7 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths)
 | 
				
			|||||||
	 * lower estimated cost.
 | 
						 * lower estimated cost.
 | 
				
			||||||
	 *
 | 
						 *
 | 
				
			||||||
	 * We also make some effort to detect directly redundant input paths, as
 | 
						 * We also make some effort to detect directly redundant input paths, as
 | 
				
			||||||
	 * can happen if there are multiple possibly usable indexes.  For this we
 | 
						 * can happen if there are multiple possibly usable indexes.  We
 | 
				
			||||||
	 * look only at plain IndexPath inputs, not at sub-OR clauses. And we
 | 
					 | 
				
			||||||
	 * consider an index redundant if any of its index conditions were already
 | 
						 * consider an index redundant if any of its index conditions were already
 | 
				
			||||||
	 * used by earlier indexes.  (We could use predicate_implied_by to have a
 | 
						 * used by earlier indexes.  (We could use predicate_implied_by to have a
 | 
				
			||||||
	 * more intelligent, but much more expensive, check --- but in most cases
 | 
						 * more intelligent, but much more expensive, check --- but in most cases
 | 
				
			||||||
@@ -569,36 +565,31 @@ choose_bitmap_and(PlannerInfo *root, RelOptInfo *rel, List *paths)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	paths = list_make1(patharray[0]);
 | 
						paths = list_make1(patharray[0]);
 | 
				
			||||||
	costsofar = bitmap_and_cost_est(root, rel, paths);
 | 
						costsofar = bitmap_and_cost_est(root, rel, paths);
 | 
				
			||||||
	if (IsA(patharray[0], IndexPath))
 | 
						qualsofar = pull_indexpath_quals(patharray[0]);
 | 
				
			||||||
		qualsofar = list_copy(((IndexPath *) patharray[0])->indexclauses);
 | 
					 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		qualsofar = NIL;
 | 
					 | 
				
			||||||
	lastcell = list_head(paths);	/* for quick deletions */
 | 
						lastcell = list_head(paths);	/* for quick deletions */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 1; i < npaths; i++)
 | 
						for (i = 1; i < npaths; i++)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		Path	   *newpath = patharray[i];
 | 
							Path	   *newpath = patharray[i];
 | 
				
			||||||
		List	   *newqual = NIL;
 | 
							List	   *newqual;
 | 
				
			||||||
		Cost		newcost;
 | 
							Cost		newcost;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (IsA(newpath, IndexPath))
 | 
							newqual = pull_indexpath_quals(newpath);
 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			newqual = ((IndexPath *) newpath)->indexclauses;
 | 
					 | 
				
			||||||
		if (lists_intersect_ptr(newqual, qualsofar))
 | 
							if (lists_intersect_ptr(newqual, qualsofar))
 | 
				
			||||||
				continue;		/* redundant */
 | 
								continue;			/* consider it redundant */
 | 
				
			||||||
		}
 | 
							/* tentatively add newpath to paths, so we can estimate cost */
 | 
				
			||||||
 | 
					 | 
				
			||||||
		paths = lappend(paths, newpath);
 | 
							paths = lappend(paths, newpath);
 | 
				
			||||||
		newcost = bitmap_and_cost_est(root, rel, paths);
 | 
							newcost = bitmap_and_cost_est(root, rel, paths);
 | 
				
			||||||
		if (newcost < costsofar)
 | 
							if (newcost < costsofar)
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
 | 
								/* keep newpath in paths, update subsidiary variables */
 | 
				
			||||||
			costsofar = newcost;
 | 
								costsofar = newcost;
 | 
				
			||||||
			if (newqual)
 | 
								qualsofar = list_concat(qualsofar, newqual);
 | 
				
			||||||
				qualsofar = list_concat(qualsofar, list_copy(newqual));
 | 
					 | 
				
			||||||
			lastcell = lnext(lastcell);
 | 
								lastcell = lnext(lastcell);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		else
 | 
							else
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
 | 
								/* reject newpath, remove it from paths list */
 | 
				
			||||||
			paths = list_delete_cell(paths, lnext(lastcell), lastcell);
 | 
								paths = list_delete_cell(paths, lnext(lastcell), lastcell);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		Assert(lnext(lastcell) == NULL);
 | 
							Assert(lnext(lastcell) == NULL);
 | 
				
			||||||
@@ -665,6 +656,62 @@ bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths)
 | 
				
			|||||||
	return bpath.total_cost;
 | 
						return bpath.total_cost;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * pull_indexpath_quals
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Given the Path structure for a plain or bitmap indexscan, extract a
 | 
				
			||||||
 | 
					 * list of RestrictInfo nodes for all the indexquals used in the Path.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This is sort of a simplified version of make_restrictinfo_from_bitmapqual;
 | 
				
			||||||
 | 
					 * here, we are not trying to produce an accurate representation of the AND/OR
 | 
				
			||||||
 | 
					 * semantics of the Path, but just find out all the base conditions used.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The result list contains pointers to the RestrictInfos used in the Path,
 | 
				
			||||||
 | 
					 * but all the list cells are freshly built, so it's safe to destructively
 | 
				
			||||||
 | 
					 * modify the list (eg, by concat'ing it with other lists).
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static List *
 | 
				
			||||||
 | 
					pull_indexpath_quals(Path *bitmapqual)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						List	   *result = NIL;
 | 
				
			||||||
 | 
						ListCell   *l;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (IsA(bitmapqual, BitmapAndPath))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							BitmapAndPath *apath = (BitmapAndPath *) bitmapqual;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							foreach(l, apath->bitmapquals)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								List	   *sublist;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								sublist = pull_indexpath_quals((Path *) lfirst(l));
 | 
				
			||||||
 | 
								result = list_concat(result, sublist);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else if (IsA(bitmapqual, BitmapOrPath))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							BitmapOrPath *opath = (BitmapOrPath *) bitmapqual;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							foreach(l, opath->bitmapquals)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								List	   *sublist;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								sublist = pull_indexpath_quals((Path *) lfirst(l));
 | 
				
			||||||
 | 
								result = list_concat(result, sublist);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else if (IsA(bitmapqual, IndexPath))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							IndexPath  *ipath = (IndexPath *) bitmapqual;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							result = list_copy(ipath->indexclauses);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							elog(ERROR, "unrecognized node type: %d", nodeTag(bitmapqual));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return result;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * lists_intersect_ptr
 | 
					 * lists_intersect_ptr
 | 
				
			||||||
@@ -1182,20 +1229,24 @@ best_inner_indexscan(PlannerInfo *root, RelOptInfo *rel,
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Find all the relevant join clauses.
 | 
						 * Find all the relevant restriction and join clauses.
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * Note: because we include restriction clauses, we will find indexscans
 | 
				
			||||||
 | 
						 * that could be plain indexscans, ie, they don't require the join context
 | 
				
			||||||
 | 
						 * at all.  This may seem redundant, but we need to include those scans in
 | 
				
			||||||
 | 
						 * the input given to choose_bitmap_and() to be sure we find optimal AND
 | 
				
			||||||
 | 
						 * combinations of join and non-join scans.  The worst case is that we
 | 
				
			||||||
 | 
						 * might return a "best inner indexscan" that's really just a plain
 | 
				
			||||||
 | 
						 * indexscan, causing some redundant effort in joinpath.c.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	clause_list = find_clauses_for_join(root, rel, outer_relids, isouterjoin);
 | 
						clause_list = find_clauses_for_join(root, rel, outer_relids, isouterjoin);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * Find all the index paths that are usable for this join, except for
 | 
						 * Find all the index paths that are usable for this join, except for
 | 
				
			||||||
	 * stuff involving OR clauses.  We can use both
 | 
						 * stuff involving OR clauses.
 | 
				
			||||||
	 * join and restriction clauses as indexquals, but we insist the path
 | 
					 | 
				
			||||||
	 * use at least one join clause (else it'd not be an "inner indexscan"
 | 
					 | 
				
			||||||
	 * but a plain indexscan, and those have already been considered).
 | 
					 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	indexpaths = find_usable_indexes(root, rel,
 | 
						indexpaths = find_usable_indexes(root, rel,
 | 
				
			||||||
									 clause_list,
 | 
														 clause_list, NIL,
 | 
				
			||||||
									 rel->baserestrictinfo,
 | 
					 | 
				
			||||||
									 false, true,
 | 
														 false, true,
 | 
				
			||||||
									 outer_relids);
 | 
														 outer_relids);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1204,8 +1255,7 @@ best_inner_indexscan(PlannerInfo *root, RelOptInfo *rel,
 | 
				
			|||||||
	 * clause list.
 | 
						 * clause list.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	bitindexpaths = generate_bitmap_or_paths(root, rel,
 | 
						bitindexpaths = generate_bitmap_or_paths(root, rel,
 | 
				
			||||||
											 clause_list,
 | 
																 clause_list, NIL,
 | 
				
			||||||
											 rel->baserestrictinfo,
 | 
					 | 
				
			||||||
											 true,
 | 
																 true,
 | 
				
			||||||
											 outer_relids);
 | 
																 outer_relids);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1255,12 +1305,13 @@ best_inner_indexscan(PlannerInfo *root, RelOptInfo *rel,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * find_clauses_for_join
 | 
					 * find_clauses_for_join
 | 
				
			||||||
 *	  Generate a list of join clauses that are potentially useful for
 | 
					 *	  Generate a list of clauses that are potentially useful for
 | 
				
			||||||
 *	  scanning rel as the inner side of a nestloop join.
 | 
					 *	  scanning rel as the inner side of a nestloop join.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Any joinclause that uses only otherrels in the specified outer_relids is
 | 
					 * We consider both join and restriction clauses.  Any joinclause that uses
 | 
				
			||||||
 * fair game.  Note that restriction clauses on rel can also be used in
 | 
					 * only otherrels in the specified outer_relids is fair game.  But there must
 | 
				
			||||||
 * forming index conditions, but we do not include those here.
 | 
					 * be at least one such joinclause in the final list, otherwise we return NIL
 | 
				
			||||||
 | 
					 * indicating that there isn't any potential win here.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
static List *
 | 
					static List *
 | 
				
			||||||
find_clauses_for_join(PlannerInfo *root, RelOptInfo *rel,
 | 
					find_clauses_for_join(PlannerInfo *root, RelOptInfo *rel,
 | 
				
			||||||
@@ -1288,28 +1339,28 @@ find_clauses_for_join(PlannerInfo *root, RelOptInfo *rel,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	bms_free(join_relids);
 | 
						bms_free(join_relids);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* quick exit if no join clause was matched */
 | 
						/* if no join clause was matched then forget it, per comments above */
 | 
				
			||||||
	if (clause_list == NIL)
 | 
						if (clause_list == NIL)
 | 
				
			||||||
		return NIL;
 | 
							return NIL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * We can also use any plain restriction clauses for the rel.  We put
 | 
				
			||||||
 | 
						 * these at the front of the clause list for the convenience of
 | 
				
			||||||
 | 
						 * remove_redundant_join_clauses, which can never remove non-join clauses
 | 
				
			||||||
 | 
						 * and hence won't be able to get rid of a non-join clause if it appears
 | 
				
			||||||
 | 
						 * after a join clause it is redundant with.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						clause_list = list_concat(list_copy(rel->baserestrictinfo), clause_list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/*
 | 
						/*
 | 
				
			||||||
	 * We may now have clauses that are known redundant.  Get rid of 'em.
 | 
						 * We may now have clauses that are known redundant.  Get rid of 'em.
 | 
				
			||||||
	 */
 | 
						 */
 | 
				
			||||||
	if (list_length(clause_list) > 1)
 | 
						if (list_length(clause_list) > 1)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
		clause_list = remove_redundant_join_clauses(root,
 | 
							clause_list = remove_redundant_join_clauses(root,
 | 
				
			||||||
													clause_list,
 | 
																		clause_list,
 | 
				
			||||||
													isouterjoin);
 | 
																		isouterjoin);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
	 * We might have found join clauses that are known redundant with
 | 
					 | 
				
			||||||
	 * restriction clauses on rel (due to conclusions drawn by implied
 | 
					 | 
				
			||||||
	 * equality deduction; without that, this would obviously never happen).
 | 
					 | 
				
			||||||
	 * Get rid of them too.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	if (rel->baserestrictinfo != NIL)
 | 
					 | 
				
			||||||
		clause_list = select_nonredundant_join_clauses(root, clause_list,
 | 
					 | 
				
			||||||
													   rel->baserestrictinfo,
 | 
					 | 
				
			||||||
													   isouterjoin);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return clause_list;
 | 
						return clause_list;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user