mirror of
https://github.com/postgres/postgres.git
synced 2025-07-02 09:02:37 +03:00
Fix a bunch of bad interactions between partial indexes and the new
planning logic for bitmap indexscans. Partial indexes create corner cases in which a scan might be done with no explicit index qual conditions, and the code wasn't handling those cases nicely. Also be a little tenser about eliminating redundant clauses in the generated plan. Per report from Dmitry Karasik.
This commit is contained in:
@ -9,7 +9,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.186 2005/07/02 23:00:40 tgl Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.187 2005/07/28 20:26:20 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -212,6 +212,8 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel)
|
||||
* subclause to any index on x alone, because such a Path would already have
|
||||
* been generated at the upper level. So we could use an index on x,y,z
|
||||
* or an index on x,y for the OR subclause, but not an index on just x.
|
||||
* When dealing with a partial index, a match of the index predicate to
|
||||
* one of the "current" clauses also makes the index usable.
|
||||
*
|
||||
* If istoplevel is true (indicating we are considering the top level of a
|
||||
* rel's restriction clauses), we will include indexes in the result that
|
||||
@ -248,6 +250,8 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
|
||||
List *restrictclauses;
|
||||
List *index_pathkeys;
|
||||
List *useful_pathkeys;
|
||||
bool useful_predicate;
|
||||
bool found_clause;
|
||||
bool index_is_ordered;
|
||||
|
||||
/*
|
||||
@ -258,29 +262,60 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
|
||||
* Otherwise, we have to test whether the added clauses are
|
||||
* sufficient to imply the predicate. If so, we could use
|
||||
* the index in the current context.
|
||||
*
|
||||
* We set useful_predicate to true iff the predicate was proven
|
||||
* using the current set of clauses. This is needed to prevent
|
||||
* matching a predOK index to an arm of an OR, which would be
|
||||
* a legal but pointlessly inefficient plan. (A better plan will
|
||||
* be generated by just scanning the predOK index alone, no OR.)
|
||||
*/
|
||||
if (index->indpred != NIL && !index->predOK)
|
||||
useful_predicate = false;
|
||||
if (index->indpred != NIL)
|
||||
{
|
||||
if (istoplevel)
|
||||
continue; /* no point in trying to prove it */
|
||||
if (index->predOK)
|
||||
{
|
||||
if (istoplevel)
|
||||
{
|
||||
/* we know predicate was proven from these clauses */
|
||||
useful_predicate = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (istoplevel)
|
||||
continue; /* no point in trying to prove it */
|
||||
|
||||
/* Form all_clauses if not done already */
|
||||
if (all_clauses == NIL)
|
||||
all_clauses = list_concat(list_copy(clauses),
|
||||
outer_clauses);
|
||||
/* Form all_clauses if not done already */
|
||||
if (all_clauses == NIL)
|
||||
all_clauses = list_concat(list_copy(clauses),
|
||||
outer_clauses);
|
||||
|
||||
if (!predicate_implied_by(index->indpred, all_clauses) ||
|
||||
predicate_implied_by(index->indpred, outer_clauses))
|
||||
continue;
|
||||
if (!predicate_implied_by(index->indpred, all_clauses))
|
||||
continue; /* can't use it at all */
|
||||
|
||||
if (!predicate_implied_by(index->indpred, outer_clauses))
|
||||
useful_predicate = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 1. Match the index against the available restriction clauses.
|
||||
* found_clause is set true only if at least one of the current
|
||||
* clauses was used.
|
||||
*/
|
||||
restrictclauses = group_clauses_by_indexkey(index,
|
||||
clauses,
|
||||
outer_clauses,
|
||||
outer_relids);
|
||||
outer_relids,
|
||||
&found_clause);
|
||||
|
||||
/*
|
||||
* Not all index AMs support scans with no restriction clauses.
|
||||
* We can't generate a scan over an index with amoptionalkey = false
|
||||
* unless there's at least one restriction clause.
|
||||
*/
|
||||
if (restrictclauses == NIL && !index->amoptionalkey)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* 2. Compute pathkeys describing index's ordering, if any, then
|
||||
@ -300,20 +335,12 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
|
||||
|
||||
/*
|
||||
* 3. Generate an indexscan path if there are relevant restriction
|
||||
* clauses OR the index ordering is potentially useful for later
|
||||
* merging or final output ordering.
|
||||
*
|
||||
* If there is a predicate, consider it anyway since the index
|
||||
* predicate has already been found to match the query. The
|
||||
* selectivity of the predicate might alone make the index useful.
|
||||
*
|
||||
* Note: not all index AMs support scans with no restriction clauses.
|
||||
* We can't generate a scan over an index with amoptionalkey = false
|
||||
* unless there's at least one restriction clause.
|
||||
* clauses in the current clauses, OR the index ordering is
|
||||
* potentially useful for later merging or final output ordering,
|
||||
* OR the index has a predicate that was proven by the current
|
||||
* clauses.
|
||||
*/
|
||||
if (restrictclauses != NIL ||
|
||||
(index->amoptionalkey &&
|
||||
(useful_pathkeys != NIL || index->indpred != NIL)))
|
||||
if (found_clause || useful_pathkeys != NIL || useful_predicate)
|
||||
{
|
||||
ipath = create_index_path(root, index,
|
||||
restrictclauses,
|
||||
@ -627,10 +654,9 @@ bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths)
|
||||
* with one index key (in no particular order); the top list is ordered by
|
||||
* index key. (This is depended on by expand_indexqual_conditions().)
|
||||
*
|
||||
* As explained in the comments for find_usable_indexes(), we can use
|
||||
* clauses from either of the given lists, but the result is required to
|
||||
* use at least one clause from the "current clauses" list. We return
|
||||
* NIL if we don't find any such clause.
|
||||
* We can use clauses from either the current clauses or outer_clauses lists,
|
||||
* but *found_clause is set TRUE only if we used at least one clause from
|
||||
* the "current clauses" list. See find_usable_indexes() for motivation.
|
||||
*
|
||||
* outer_relids determines what Vars will be allowed on the other side
|
||||
* of a possible index qual; see match_clause_to_indexcol().
|
||||
@ -643,18 +669,25 @@ bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths)
|
||||
* Example: given an index on (A,B,C), we would return ((C1 C2) () (C3 C4))
|
||||
* if we find that clauses C1 and C2 use column A, clauses C3 and C4 use
|
||||
* column C, and no clauses use column B.
|
||||
*
|
||||
* Note: in some circumstances we may find the same RestrictInfos coming
|
||||
* from multiple places. Defend against redundant outputs by using
|
||||
* list_append_unique_ptr (pointer equality should be good enough).
|
||||
*/
|
||||
List *
|
||||
group_clauses_by_indexkey(IndexOptInfo *index,
|
||||
List *clauses, List *outer_clauses,
|
||||
Relids outer_relids)
|
||||
Relids outer_relids,
|
||||
bool *found_clause)
|
||||
{
|
||||
List *clausegroup_list = NIL;
|
||||
bool found_clause = false;
|
||||
bool found_outer_clause = false;
|
||||
int indexcol = 0;
|
||||
Oid *classes = index->classlist;
|
||||
|
||||
if (clauses == NIL)
|
||||
*found_clause = false; /* default result */
|
||||
|
||||
if (clauses == NIL && outer_clauses == NIL)
|
||||
return NIL; /* cannot succeed */
|
||||
|
||||
do
|
||||
@ -675,8 +708,8 @@ group_clauses_by_indexkey(IndexOptInfo *index,
|
||||
rinfo,
|
||||
outer_relids))
|
||||
{
|
||||
clausegroup = lappend(clausegroup, rinfo);
|
||||
found_clause = true;
|
||||
clausegroup = list_append_unique_ptr(clausegroup, rinfo);
|
||||
*found_clause = true;
|
||||
}
|
||||
}
|
||||
|
||||
@ -691,7 +724,10 @@ group_clauses_by_indexkey(IndexOptInfo *index,
|
||||
curClass,
|
||||
rinfo,
|
||||
outer_relids))
|
||||
clausegroup = lappend(clausegroup, rinfo);
|
||||
{
|
||||
clausegroup = list_append_unique_ptr(clausegroup, rinfo);
|
||||
found_outer_clause = true;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -707,8 +743,8 @@ group_clauses_by_indexkey(IndexOptInfo *index,
|
||||
|
||||
} while (!DoneMatchingIndexKeys(classes));
|
||||
|
||||
if (!found_clause)
|
||||
return NIL;
|
||||
if (!*found_clause && !found_outer_clause)
|
||||
return NIL; /* no indexable clauses anywhere */
|
||||
|
||||
return clausegroup_list;
|
||||
}
|
||||
|
Reference in New Issue
Block a user