return;
}
+ /*
+ * Test any partial indexes of rel for applicability. We must do this
+ * first since partial unique indexes can affect size estimates.
+ */
+ check_partial_indexes(root, rel);
+
/* Mark rel with estimated output rows, width, etc */
set_baserel_size_estimates(root, rel);
- /* Test any partial indexes of rel for applicability */
- check_partial_indexes(root, rel);
-
/*
* Check to see if we can extract any restriction conditions from join
* quals that are OR-of-AND structures. If so, add them to the rel's
- * restriction list, and recompute the size estimates.
+ * restriction list, and redo the above steps.
*/
if (create_or_index_quals(root, rel))
+ {
+ check_partial_indexes(root, rel);
set_baserel_size_estimates(root, rel);
+ }
/*
* Generate paths and add them to the rel's pathlist.
/*
* check_partial_indexes
- * Check each partial index of the relation, and mark it predOK or not
- * depending on whether the predicate is satisfied for this query.
+ * Check each partial index of the relation, and mark it predOK if
+ * the index's predicate is satisfied for this query.
+ *
+ * Note: it is possible for this to get re-run after adding more restrictions
+ * to the rel; so we might be able to prove more indexes OK. We assume that
+ * adding more restrictions can't make an index not OK.
*/
void
check_partial_indexes(PlannerInfo *root, RelOptInfo *rel)
if (index->indpred == NIL)
continue; /* ignore non-partial indexes */
+ if (index->predOK)
+ continue; /* don't repeat work if already proven OK */
+
index->predOK = predicate_implied_by(index->indpred,
restrictinfo_list);
}
/*
* Note: ignore partial indexes, since they don't allow us to conclude
- * that all attr values are distinct. We don't take any interest in
- * expressional indexes either. Also, a multicolumn unique index
- * doesn't allow us to conclude that just the specified attr is
- * unique.
+ * that all attr values are distinct, *unless* they are marked predOK
+ * which means we know the index's predicate is satisfied by the query.
+ * We don't take any interest in expressional indexes either. Also, a
+ * multicolumn unique index doesn't allow us to conclude that just the
+ * specified attr is unique.
*/
if (index->unique &&
index->ncolumns == 1 &&
index->indexkeys[0] == attno &&
- index->indpred == NIL)
+ (index->indpred == NIL || index->predOK))
return true;
}
return false;
if (constisnull)
return 0.0;
+ /*
+ * If we matched the var to a unique index, assume there is exactly one
+ * match regardless of anything else. (This is slightly bogus, since
+ * the index's equality operator might be different from ours, but it's
+ * more likely to be right than ignoring the information.)
+ */
+ if (vardata->isunique && vardata->rel && vardata->rel->tuples >= 1.0)
+ return 1.0 / vardata->rel->tuples;
+
if (HeapTupleIsValid(vardata->statsTuple))
{
Form_pg_statistic stats;
{
double selec;
+ /*
+ * If we matched the var to a unique index, assume there is exactly one
+ * match regardless of anything else. (This is slightly bogus, since
+ * the index's equality operator might be different from ours, but it's
+ * more likely to be right than ignoring the information.)
+ */
+ if (vardata->isunique && vardata->rel && vardata->rel->tuples >= 1.0)
+ return 1.0 / vardata->rel->tuples;
+
if (HeapTupleIsValid(vardata->statsTuple))
{
Form_pg_statistic stats;
* atttype, atttypmod: type data to pass to get_attstatsslot(). This is
* commonly the same as the exposed type of the variable argument,
* but can be different in binary-compatible-type cases.
+ * isunique: TRUE if we were able to match the var to a unique index,
+ * implying its values are unique for this query.
*
* Caller is responsible for doing ReleaseVariableStats() before exiting.
*/
vardata->rel = find_base_rel(root, var->varno);
vardata->atttype = var->vartype;
vardata->atttypmod = var->vartypmod;
+ vardata->isunique = has_unique_index(vardata->rel, var->varattno);
rte = root->simple_rte_array[var->varno];
if (indexpr_item == NULL)
continue; /* no expressions here... */
- /*
- * Ignore partial indexes since they probably don't reflect
- * whole-relation statistics. Possibly reconsider this later.
- */
- if (index->indpred)
- continue;
-
for (pos = 0; pos < index->ncolumns; pos++)
{
if (index->indexkeys[pos] == 0)
*/
if (index->unique &&
index->ncolumns == 1 &&
- index->indpred == NIL)
+ (index->indpred == NIL || index->predOK))
vardata->isunique = true;
- /* Has it got stats? */
+
+ /*
+ * Has it got stats? We only consider stats for
+ * non-partial indexes, since partial indexes
+ * probably don't reflect whole-relation statistics;
+ * the above check for uniqueness is the only info
+ * we take from a partial index.
+ *
+ * An index stats hook, however, must make its own
+ * decisions about what to do with partial indexes.
+ */
if (get_index_stats_hook &&
(*get_index_stats_hook) (root, index->indexoid,
pos + 1, vardata))
!vardata->freefunc)
elog(ERROR, "no function provided to release variable stats with");
}
- else
+ else if (index->indpred == NIL)
{
vardata->statsTuple =
SearchSysCache(STATRELATT,
/*
* If there is a unique index for the variable, assume it is unique no
- * matter what pg_statistic says (the statistics could be out of date).
- * Can skip search if we already think it's unique.
+ * matter what pg_statistic says; the statistics could be out of date,
+ * or we might have found a partial unique index that proves the var
+ * is unique for this query.
*/
- if (stadistinct != -1.0)
- {
- if (vardata->isunique)
- stadistinct = -1.0;
- else if (vardata->var && IsA(vardata->var, Var) &&
- vardata->rel &&
- has_unique_index(vardata->rel,
- ((Var *) vardata->var)->varattno))
- stadistinct = -1.0;
- }
+ if (vardata->isunique)
+ stadistinct = -1.0;
/*
* If we had an absolute estimate, use that.