Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit ce6e31d

Browse files
committed
Teach the planner to treat a partial unique index as proving a variable is
unique for a particular query, if the index predicate is satisfied. This requires a bit of reordering of operations so that we check the predicates before doing any selectivity estimates, but shouldn't really cause any noticeable slowdown. Per a comment from Michal Politowski.
1 parent 57b5984 commit ce6e31d

File tree

5 files changed

+70
-39
lines changed

5 files changed

+70
-39
lines changed

src/backend/optimizer/path/allpaths.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.179 2009/01/01 17:23:43 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.180 2009/02/15 20:16:21 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -225,19 +225,25 @@ set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
225225
return;
226226
}
227227

228+
/*
229+
* Test any partial indexes of rel for applicability. We must do this
230+
* first since partial unique indexes can affect size estimates.
231+
*/
232+
check_partial_indexes(root, rel);
233+
228234
/* Mark rel with estimated output rows, width, etc */
229235
set_baserel_size_estimates(root, rel);
230236

231-
/* Test any partial indexes of rel for applicability */
232-
check_partial_indexes(root, rel);
233-
234237
/*
235238
* Check to see if we can extract any restriction conditions from join
236239
* quals that are OR-of-AND structures. If so, add them to the rel's
237-
* restriction list, and recompute the size estimates.
240+
* restriction list, and redo the above steps.
238241
*/
239242
if (create_or_index_quals(root, rel))
243+
{
244+
check_partial_indexes(root, rel);
240245
set_baserel_size_estimates(root, rel);
246+
}
241247

242248
/*
243249
* Generate paths and add them to the rel's pathlist.

src/backend/optimizer/path/indxpath.c

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.235 2009/01/01 17:23:43 momjian Exp $
12+
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.236 2009/02/15 20:16:21 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -1367,8 +1367,12 @@ match_rowcompare_to_indexcol(IndexOptInfo *index,
13671367

13681368
/*
13691369
* check_partial_indexes
1370-
* Check each partial index of the relation, and mark it predOK or not
1371-
* depending on whether the predicate is satisfied for this query.
1370+
* Check each partial index of the relation, and mark it predOK if
1371+
* the index's predicate is satisfied for this query.
1372+
*
1373+
* Note: it is possible for this to get re-run after adding more restrictions
1374+
* to the rel; so we might be able to prove more indexes OK. We assume that
1375+
* adding more restrictions can't make an index not OK.
13721376
*/
13731377
void
13741378
check_partial_indexes(PlannerInfo *root, RelOptInfo *rel)
@@ -1383,6 +1387,9 @@ check_partial_indexes(PlannerInfo *root, RelOptInfo *rel)
13831387
if (index->indpred == NIL)
13841388
continue; /* ignore non-partial indexes */
13851389

1390+
if (index->predOK)
1391+
continue; /* don't repeat work if already proven OK */
1392+
13861393
index->predOK = predicate_implied_by(index->indpred,
13871394
restrictinfo_list);
13881395
}

src/backend/optimizer/path/orindxpath.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.87 2009/02/06 23:43:23 tgl Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.88 2009/02/15 20:16:21 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -181,6 +181,6 @@ create_or_index_quals(PlannerInfo *root, RelOptInfo *rel)
181181
/* It isn't an outer join clause, so no need to adjust outer_selec */
182182
}
183183

184-
/* Tell caller to recompute rel's rows estimate */
184+
/* Tell caller to recompute partial index status and rowcount estimate */
185185
return true;
186186
}

src/backend/optimizer/util/plancat.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.154 2009/01/07 22:40:49 tgl Exp $
12+
* $PostgreSQL: pgsql/src/backend/optimizer/util/plancat.c,v 1.155 2009/02/15 20:16:21 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -939,15 +939,16 @@ has_unique_index(RelOptInfo *rel, AttrNumber attno)
939939

940940
/*
941941
* Note: ignore partial indexes, since they don't allow us to conclude
942-
* that all attr values are distinct. We don't take any interest in
943-
* expressional indexes either. Also, a multicolumn unique index
944-
* doesn't allow us to conclude that just the specified attr is
945-
* unique.
942+
* that all attr values are distinct, *unless* they are marked predOK
943+
* which means we know the index's predicate is satisfied by the query.
944+
* We don't take any interest in expressional indexes either. Also, a
945+
* multicolumn unique index doesn't allow us to conclude that just the
946+
* specified attr is unique.
946947
*/
947948
if (index->unique &&
948949
index->ncolumns == 1 &&
949950
index->indexkeys[0] == attno &&
950-
index->indpred == NIL)
951+
(index->indpred == NIL || index->predOK))
951952
return true;
952953
}
953954
return false;

src/backend/utils/adt/selfuncs.c

Lines changed: 40 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*
1616
*
1717
* IDENTIFICATION
18-
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.258 2009/01/01 17:23:50 momjian Exp $
18+
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.259 2009/02/15 20:16:21 tgl Exp $
1919
*
2020
*-------------------------------------------------------------------------
2121
*/
@@ -234,6 +234,15 @@ var_eq_const(VariableStatData *vardata, Oid operator,
234234
if (constisnull)
235235
return 0.0;
236236

237+
/*
238+
* If we matched the var to a unique index, assume there is exactly one
239+
* match regardless of anything else. (This is slightly bogus, since
240+
* the index's equality operator might be different from ours, but it's
241+
* more likely to be right than ignoring the information.)
242+
*/
243+
if (vardata->isunique && vardata->rel && vardata->rel->tuples >= 1.0)
244+
return 1.0 / vardata->rel->tuples;
245+
237246
if (HeapTupleIsValid(vardata->statsTuple))
238247
{
239248
Form_pg_statistic stats;
@@ -357,6 +366,15 @@ var_eq_non_const(VariableStatData *vardata, Oid operator,
357366
{
358367
double selec;
359368

369+
/*
370+
* If we matched the var to a unique index, assume there is exactly one
371+
* match regardless of anything else. (This is slightly bogus, since
372+
* the index's equality operator might be different from ours, but it's
373+
* more likely to be right than ignoring the information.)
374+
*/
375+
if (vardata->isunique && vardata->rel && vardata->rel->tuples >= 1.0)
376+
return 1.0 / vardata->rel->tuples;
377+
360378
if (HeapTupleIsValid(vardata->statsTuple))
361379
{
362380
Form_pg_statistic stats;
@@ -3969,6 +3987,8 @@ get_join_variables(PlannerInfo *root, List *args, SpecialJoinInfo *sjinfo,
39693987
* atttype, atttypmod: type data to pass to get_attstatsslot(). This is
39703988
* commonly the same as the exposed type of the variable argument,
39713989
* but can be different in binary-compatible-type cases.
3990+
* isunique: TRUE if we were able to match the var to a unique index,
3991+
* implying its values are unique for this query.
39723992
*
39733993
* Caller is responsible for doing ReleaseVariableStats() before exiting.
39743994
*/
@@ -4005,6 +4025,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
40054025
vardata->rel = find_base_rel(root, var->varno);
40064026
vardata->atttype = var->vartype;
40074027
vardata->atttypmod = var->vartypmod;
4028+
vardata->isunique = has_unique_index(vardata->rel, var->varattno);
40084029

40094030
rte = root->simple_rte_array[var->varno];
40104031

@@ -4121,13 +4142,6 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
41214142
if (indexpr_item == NULL)
41224143
continue; /* no expressions here... */
41234144

4124-
/*
4125-
* Ignore partial indexes since they probably don't reflect
4126-
* whole-relation statistics. Possibly reconsider this later.
4127-
*/
4128-
if (index->indpred)
4129-
continue;
4130-
41314145
for (pos = 0; pos < index->ncolumns; pos++)
41324146
{
41334147
if (index->indexkeys[pos] == 0)
@@ -4147,9 +4161,19 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
41474161
*/
41484162
if (index->unique &&
41494163
index->ncolumns == 1 &&
4150-
index->indpred == NIL)
4164+
(index->indpred == NIL || index->predOK))
41514165
vardata->isunique = true;
4152-
/* Has it got stats? */
4166+
4167+
/*
4168+
* Has it got stats? We only consider stats for
4169+
* non-partial indexes, since partial indexes
4170+
* probably don't reflect whole-relation statistics;
4171+
* the above check for uniqueness is the only info
4172+
* we take from a partial index.
4173+
*
4174+
* An index stats hook, however, must make its own
4175+
* decisions about what to do with partial indexes.
4176+
*/
41534177
if (get_index_stats_hook &&
41544178
(*get_index_stats_hook) (root, index->indexoid,
41554179
pos + 1, vardata))
@@ -4163,7 +4187,7 @@ examine_variable(PlannerInfo *root, Node *node, int varRelid,
41634187
!vardata->freefunc)
41644188
elog(ERROR, "no function provided to release variable stats with");
41654189
}
4166-
else
4190+
else if (index->indpred == NIL)
41674191
{
41684192
vardata->statsTuple =
41694193
SearchSysCache(STATRELATT,
@@ -4254,19 +4278,12 @@ get_variable_numdistinct(VariableStatData *vardata)
42544278

42554279
/*
42564280
* If there is a unique index for the variable, assume it is unique no
4257-
* matter what pg_statistic says (the statistics could be out of date).
4258-
* Can skip search if we already think it's unique.
4281+
* matter what pg_statistic says; the statistics could be out of date,
4282+
* or we might have found a partial unique index that proves the var
4283+
* is unique for this query.
42594284
*/
4260-
if (stadistinct != -1.0)
4261-
{
4262-
if (vardata->isunique)
4263-
stadistinct = -1.0;
4264-
else if (vardata->var && IsA(vardata->var, Var) &&
4265-
vardata->rel &&
4266-
has_unique_index(vardata->rel,
4267-
((Var *) vardata->var)->varattno))
4268-
stadistinct = -1.0;
4269-
}
4285+
if (vardata->isunique)
4286+
stadistinct = -1.0;
42704287

42714288
/*
42724289
* If we had an absolute estimate, use that.

0 commit comments

Comments
 (0)