1
0
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:
Tom Lane
2005-07-28 20:26:22 +00:00
parent 3535cb827a
commit a4ca842319
9 changed files with 399 additions and 83 deletions

View File

@ -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;
}