*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.217 2007/03/17 00:11:04 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.218 2007/03/21 22:18:12 tgl Exp $
*
*-------------------------------------------------------------------------
*/
static int bitmap_path_comparator(const void *a, const void *b);
static Cost bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel,
List *paths, RelOptInfo *outer_rel);
-static List *pull_indexpath_quals(Path *bitmapqual);
-static bool lists_intersect_ptr(List *list1, List *list2);
+static void find_indexpath_quals(Path *bitmapqual, List **quals, List **preds);
+static bool lists_intersect(List *list1, List *list2);
static bool match_clause_to_indexcol(IndexOptInfo *index,
int indexcol, Oid opfamily,
RestrictInfo *rinfo,
Path **patharray;
Cost costsofar;
List *qualsofar;
+ List *firstpred;
ListCell *lastcell;
int i;
ListCell *l;
* 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
* more intelligent, but much more expensive, check --- but in most cases
- * simple pointer equality should suffice, since after all the index
- * conditions are all coming from the same RestrictInfo lists.)
+ * simple equality should suffice.)
*
* You might think the condition for redundancy should be "all index
* conditions already used", not "any", but this turns out to be wrong.
* non-selective. In any case, we'd surely be drastically misestimating
* the selectivity if we count the same condition twice.
*
- * We include index predicate conditions in the redundancy test. Because
- * the test is just for pointer equality and not equal(), the effect is
- * that use of the same partial index in two different AND elements is
- * considered redundant. (XXX is this too strong?)
+ * We must also consider index predicate conditions in checking for
+ * redundancy, because the estimated selectivity of a partial index
+ * includes its predicate even if the explicit index conditions don't.
+ * Here we have to work harder than just checking expression equality:
+ * we check to see if any of the predicate clauses are implied by
+ * index conditions or predicate clauses of previous paths. 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".
+ * Also, we reject indexes that have a qual condition matching any
+ * previously-used index's predicate (by including predicate conditions
+ * into qualsofar). It should be sufficient to check equality in this
+ * case, not implication, since we've sorted the paths by selectivity
+ * and so tighter conditions are seen first --- only for exactly equal
+ * cases might the partial index come first.
+ *
+ * XXX the reason we need all these redundancy checks is that 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 look like it reduces the total
+ * cost. Perhaps someday that code will be smarter and we can remove
+ * these heuristics.
*
* Note: outputting the selected sub-paths in selectivity order is a good
* thing even if we weren't using that as part of the selection method,
paths = list_make1(patharray[0]);
costsofar = bitmap_and_cost_est(root, rel, paths, outer_rel);
- qualsofar = pull_indexpath_quals(patharray[0]);
+ find_indexpath_quals(patharray[0], &qualsofar, &firstpred);
+ qualsofar = list_concat(qualsofar, firstpred);
lastcell = list_head(paths); /* for quick deletions */
for (i = 1; i < npaths; i++)
{
Path *newpath = patharray[i];
List *newqual;
+ List *newpred;
Cost newcost;
- newqual = pull_indexpath_quals(newpath);
- if (lists_intersect_ptr(newqual, qualsofar))
+ find_indexpath_quals(newpath, &newqual, &newpred);
+ if (lists_intersect(newqual, qualsofar))
continue; /* consider it redundant */
+ if (newpred)
+ {
+ bool redundant = false;
+
+ /* we check each predicate clause separately */
+ foreach(l, newpred)
+ {
+ Node *np = (Node *) lfirst(l);
+
+ if (predicate_implied_by(list_make1(np), qualsofar))
+ {
+ redundant = true;
+ break; /* out of inner loop */
+ }
+ }
+ if (redundant)
+ continue;
+ }
/* tentatively add newpath to paths, so we can estimate cost */
paths = lappend(paths, newpath);
newcost = bitmap_and_cost_est(root, rel, paths, outer_rel);
{
/* keep newpath in paths, update subsidiary variables */
costsofar = newcost;
- qualsofar = list_concat(qualsofar, newqual);
+ qualsofar = list_concat(list_concat(qualsofar, newqual), newpred);
lastcell = lnext(lastcell);
}
else
}
/*
- * pull_indexpath_quals
+ * find_indexpath_quals
*
- * Given the Path structure for a plain or bitmap indexscan, extract a list
+ * Given the Path structure for a plain or bitmap indexscan, extract lists
* of all the indexquals and index predicate conditions 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 expressions used in the Path,
+ * The result lists contain pointers to the expressions 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).
+ * modify the lists (eg, by concat'ing with other lists).
*/
-static List *
-pull_indexpath_quals(Path *bitmapqual)
+static void
+find_indexpath_quals(Path *bitmapqual, List **quals, List **preds)
{
- List *result = NIL;
ListCell *l;
+ *quals = NIL;
+ *preds = NIL;
+
if (IsA(bitmapqual, BitmapAndPath))
{
BitmapAndPath *apath = (BitmapAndPath *) bitmapqual;
foreach(l, apath->bitmapquals)
{
- List *sublist;
+ List *subquals;
+ List *subpreds;
- sublist = pull_indexpath_quals((Path *) lfirst(l));
- result = list_concat(result, sublist);
+ find_indexpath_quals((Path *) lfirst(l), &subquals, &subpreds);
+ *quals = list_concat(*quals, subquals);
+ *preds = list_concat(*preds, subpreds);
}
}
else if (IsA(bitmapqual, BitmapOrPath))
foreach(l, opath->bitmapquals)
{
- List *sublist;
+ List *subquals;
+ List *subpreds;
- sublist = pull_indexpath_quals((Path *) lfirst(l));
- result = list_concat(result, sublist);
+ find_indexpath_quals((Path *) lfirst(l), &subquals, &subpreds);
+ *quals = list_concat(*quals, subquals);
+ *preds = list_concat(*preds, subpreds);
}
}
else if (IsA(bitmapqual, IndexPath))
{
IndexPath *ipath = (IndexPath *) bitmapqual;
- result = get_actual_clauses(ipath->indexclauses);
- result = list_concat(result, list_copy(ipath->indexinfo->indpred));
+ *quals = get_actual_clauses(ipath->indexclauses);
+ *preds = list_copy(ipath->indexinfo->indpred);
}
else
elog(ERROR, "unrecognized node type: %d", nodeTag(bitmapqual));
-
- return result;
}
/*
- * lists_intersect_ptr
+ * lists_intersect
* Detect whether two lists have a nonempty intersection,
- * using pointer equality to compare members.
+ * using equal() to compare members.
*
* This possibly should go into list.c, but it doesn't yet have any use
* except in choose_bitmap_and.
*/
static bool
-lists_intersect_ptr(List *list1, List *list2)
+lists_intersect(List *list1, List *list2)
{
ListCell *cell1;
foreach(cell2, list2)
{
- if (lfirst(cell2) == datum1)
+ if (equal(lfirst(cell2), datum1))
return true;
}
}
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.229 2007/03/17 00:11:05 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.230 2007/03/21 22:18:12 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/plancat.h"
+#include "optimizer/predtest.h"
#include "optimizer/restrictinfo.h"
#include "optimizer/var.h"
#include "parser/parse_coerce.h"
List *selectivityQuals;
ListCell *l;
- /*
+ /*----------
* If the index is partial, AND the index predicate with the explicitly
* given indexquals to produce a more accurate idea of the index
- * selectivity. This may produce redundant clauses. We get rid of exact
- * duplicates in the code below. We expect that most cases of partial
- * redundancy (such as "x < 4" from the qual and "x < 5" from the
- * predicate) will be recognized and handled correctly by
- * clauselist_selectivity(). This assumption is somewhat fragile, since
- * it depends on predicate_implied_by() and clauselist_selectivity()
- * having similar capabilities, and there are certainly many cases where
- * we will end up with a too-low selectivity estimate. This will bias the
- * system in favor of using partial indexes where possible, which is not
- * necessarily a bad thing. But it'd be nice to do better someday.
+ * selectivity. However, we need to be careful not to insert redundant
+ * clauses, because clauselist_selectivity() is easily fooled into
+ * computing a too-low selectivity estimate. Our approach is to add
+ * only the index predicate clause(s) that cannot be proven to be implied
+ * by the given indexquals. This successfully handles cases such as a
+ * qual "x = 42" used with a partial index "WHERE x >= 40 AND x < 50".
+ * There are many other cases where we won't detect redundancy, leading
+ * to a too-low selectivity estimate, which will bias the system in favor
+ * of using partial indexes where possible. That is not necessarily bad
+ * though.
*
- * Note that index->indpred and indexQuals are both in implicit-AND form,
- * so ANDing them together just takes merging the lists. However,
- * eliminating duplicates is a bit trickier because indexQuals contains
- * RestrictInfo nodes and the indpred does not. It is okay to pass a
- * mixed list to clauselist_selectivity, but we have to work a bit to
- * generate a list without logical duplicates. (We could just list_union
- * indpred and strippedQuals, but then we'd not get caching of per-qual
- * selectivity estimates.)
+ * Note that indexQuals contains RestrictInfo nodes while the indpred
+ * does not. This is OK for both predicate_implied_by() and
+ * clauselist_selectivity().
+ *----------
*/
if (index->indpred != NIL)
{
- List *strippedQuals;
- List *predExtraQuals;
+ List *predExtraQuals = NIL;
+
+ foreach(l, index->indpred)
+ {
+ Node *predQual = (Node *) lfirst(l);
+ List *oneQual = list_make1(predQual);
- strippedQuals = get_actual_clauses(indexQuals);
- predExtraQuals = list_difference(index->indpred, strippedQuals);
+ if (!predicate_implied_by(oneQual, indexQuals))
+ predExtraQuals = list_concat(predExtraQuals, oneQual);
+ }
+ /* list_concat avoids modifying the passed-in indexQuals list */
selectivityQuals = list_concat(predExtraQuals, indexQuals);
}
else