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

Commit 2611285

Browse files
committed
Fix OR-index-scan planner to recognize that a partial index is usable
for scanning one term of an OR clause if the index's predicate is implied by that same OR clause term (possibly in conjunction with top-level WHERE clauses). Per recent example from Dawid Kuroczko, http://archives.postgresql.org/pgsql-performance/2004-10/msg00095.php Also, fix a very long-standing bug in index predicate testing, namely the bizarre ordering of decomposition of predicate and restriction clauses. AFAICS the correct way is to break down the predicate all the way, and then for each component term see if you can prove it from the entire restriction set. The original coding had a purely-implementation-artifact distinction between ANDing at the top level and ANDing below that, and proceeded to get the decomposition order wrong everywhere below the top level, with the result that even slightly complicated AND/OR predicates could not be proven. For instance, given create index foop on foo(f2) where f1=42 or f1=1 or (f1 = 11 and f2 = 55); the old code would fail to match this index to the query select * from foo where f1 = 11 and f2 = 55; when it obviously ought to match.
1 parent c0c4883 commit 2611285

File tree

3 files changed

+101
-74
lines changed

3 files changed

+101
-74
lines changed

src/backend/optimizer/path/indxpath.c

+67-68
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.164 2004/08/29 05:06:43 momjian Exp $
12+
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.165 2004/10/11 22:56:56 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -64,10 +64,9 @@ static bool match_join_clause_to_indexcol(RelOptInfo *rel, IndexOptInfo *index,
6464
RestrictInfo *rinfo);
6565
static Oid indexable_operator(Expr *clause, Oid opclass,
6666
bool indexkey_on_left);
67-
static bool pred_test(List *predicate_list, List *restrictinfo_list);
67+
static bool pred_test_recurse_pred(Expr *predicate, List *restrictinfo_list);
6868
static bool pred_test_restrict_list(Expr *predicate, List *restrictinfo_list);
69-
static bool pred_test_recurse_clause(Expr *predicate, Node *clause);
70-
static bool pred_test_recurse_pred(Expr *predicate, Node *clause);
69+
static bool pred_test_recurse_restrict(Expr *predicate, Node *clause);
7170
static bool pred_test_simple_clause(Expr *predicate, Node *clause);
7271
static Relids indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index);
7372
static Path *make_innerjoin_index_path(Query *root,
@@ -750,14 +749,13 @@ check_partial_indexes(Query *root, RelOptInfo *rel)
750749
* Recursively checks whether the clauses in restrictinfo_list imply
751750
* that the given predicate is true.
752751
*
753-
* This routine (together with the routines it calls) iterates over
754-
* ANDs in the predicate first, then reduces the qualification
755-
* clauses down to their constituent terms, and iterates over ORs
756-
* in the predicate last. This order is important to make the test
757-
* succeed whenever possible (assuming the predicate has been converted
758-
* to CNF format). --Nels, Jan '93
752+
* This routine (together with the routines it calls) first breaks down
753+
* the predicate to its constituent AND/OR elements, then similarly
754+
* breaks down the restriction clauses to AND/OR elements in an effort
755+
* to prove that each predicate element is implied. The top-level
756+
* List structure of each list corresponds to an AND list.
759757
*/
760-
static bool
758+
bool
761759
pred_test(List *predicate_list, List *restrictinfo_list)
762760
{
763761
ListCell *pred;
@@ -785,20 +783,63 @@ pred_test(List *predicate_list, List *restrictinfo_list)
785783
{
786784
/*
787785
* if any clause is not implied, the whole predicate is not
788-
* implied. Note we assume that any sub-ANDs have been flattened
789-
* when the predicate was fed through canonicalize_qual().
786+
* implied.
790787
*/
791-
if (!pred_test_restrict_list(lfirst(pred), restrictinfo_list))
788+
if (!pred_test_recurse_pred(lfirst(pred), restrictinfo_list))
792789
return false;
793790
}
794791
return true;
795792
}
796793

797794

798795
/*
799-
* pred_test_restrict_list
796+
* pred_test_recurse_pred
800797
* Does the "predicate inclusion test" for one conjunct of a predicate
801-
* expression.
798+
* expression. Here we recursively deal with the possibility that the
799+
* predicate conjunct is itself an AND or OR structure.
800+
*/
801+
static bool
802+
pred_test_recurse_pred(Expr *predicate, List *restrictinfo_list)
803+
{
804+
List *items;
805+
ListCell *item;
806+
807+
Assert(predicate != NULL);
808+
if (or_clause((Node *) predicate))
809+
{
810+
items = ((BoolExpr *) predicate)->args;
811+
foreach(item, items)
812+
{
813+
/* if any item is implied, the whole predicate is implied */
814+
if (pred_test_recurse_pred(lfirst(item), restrictinfo_list))
815+
return true;
816+
}
817+
return false;
818+
}
819+
else if (and_clause((Node *) predicate))
820+
{
821+
items = ((BoolExpr *) predicate)->args;
822+
foreach(item, items)
823+
{
824+
/*
825+
* if any item is not implied, the whole predicate is not
826+
* implied
827+
*/
828+
if (!pred_test_recurse_pred(lfirst(item), restrictinfo_list))
829+
return false;
830+
}
831+
return true;
832+
}
833+
else
834+
return pred_test_restrict_list(predicate, restrictinfo_list);
835+
}
836+
837+
838+
/*
839+
* pred_test_restrict_list
840+
* Does the "predicate inclusion test" for one element of a predicate
841+
* expression. Here we take care of the AND semantics of the top-level
842+
* restrictinfo list.
802843
*/
803844
static bool
804845
pred_test_restrict_list(Expr *predicate, List *restrictinfo_list)
@@ -809,23 +850,25 @@ pred_test_restrict_list(Expr *predicate, List *restrictinfo_list)
809850
{
810851
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(item);
811852

853+
Assert(IsA(restrictinfo, RestrictInfo));
854+
812855
/* if any clause implies the predicate, return true */
813-
if (pred_test_recurse_clause(predicate,
814-
(Node *) restrictinfo->clause))
856+
if (pred_test_recurse_restrict(predicate,
857+
(Node *) restrictinfo->clause))
815858
return true;
816859
}
817860
return false;
818861
}
819862

820863

821864
/*
822-
* pred_test_recurse_clause
823-
* Does the "predicate inclusion test" for a general restriction-clause
865+
* pred_test_recurse_restrict
866+
* Does the "predicate inclusion test" for one element of a predicate
824867
* expression. Here we recursively deal with the possibility that the
825-
* restriction clause is itself an AND or OR structure.
868+
* restriction-list element is itself an AND or OR structure.
826869
*/
827870
static bool
828-
pred_test_recurse_clause(Expr *predicate, Node *clause)
871+
pred_test_recurse_restrict(Expr *predicate, Node *clause)
829872
{
830873
List *items;
831874
ListCell *item;
@@ -837,7 +880,7 @@ pred_test_recurse_clause(Expr *predicate, Node *clause)
837880
foreach(item, items)
838881
{
839882
/* if any OR item doesn't imply the predicate, clause doesn't */
840-
if (!pred_test_recurse_clause(predicate, lfirst(item)))
883+
if (!pred_test_recurse_restrict(predicate, lfirst(item)))
841884
return false;
842885
}
843886
return true;
@@ -851,55 +894,11 @@ pred_test_recurse_clause(Expr *predicate, Node *clause)
851894
* if any AND item implies the predicate, the whole clause
852895
* does
853896
*/
854-
if (pred_test_recurse_clause(predicate, lfirst(item)))
897+
if (pred_test_recurse_restrict(predicate, lfirst(item)))
855898
return true;
856899
}
857900
return false;
858901
}
859-
else
860-
return pred_test_recurse_pred(predicate, clause);
861-
}
862-
863-
864-
/*
865-
* pred_test_recurse_pred
866-
* Does the "predicate inclusion test" for one conjunct of a predicate
867-
* expression for a simple restriction clause. Here we recursively deal
868-
* with the possibility that the predicate conjunct is itself an AND or
869-
* OR structure.
870-
*/
871-
static bool
872-
pred_test_recurse_pred(Expr *predicate, Node *clause)
873-
{
874-
List *items;
875-
ListCell *item;
876-
877-
Assert(predicate != NULL);
878-
if (or_clause((Node *) predicate))
879-
{
880-
items = ((BoolExpr *) predicate)->args;
881-
foreach(item, items)
882-
{
883-
/* if any item is implied, the whole predicate is implied */
884-
if (pred_test_recurse_pred(lfirst(item), clause))
885-
return true;
886-
}
887-
return false;
888-
}
889-
else if (and_clause((Node *) predicate))
890-
{
891-
items = ((BoolExpr *) predicate)->args;
892-
foreach(item, items)
893-
{
894-
/*
895-
* if any item is not implied, the whole predicate is not
896-
* implied
897-
*/
898-
if (!pred_test_recurse_pred(lfirst(item), clause))
899-
return false;
900-
}
901-
return true;
902-
}
903902
else
904903
return pred_test_simple_clause(predicate, clause);
905904
}

src/backend/optimizer/path/orindxpath.c

+32-5
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.62 2004/08/29 05:06:43 momjian Exp $
11+
* $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.63 2004/10/11 22:56:56 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -355,15 +355,42 @@ best_or_subclause_index(Query *root,
355355
List *indexquals;
356356
Path subclause_path;
357357

358-
/* Ignore partial indexes that do not match the query */
358+
/*
359+
* Ignore partial indexes that do not match the query. If predOK
360+
* is true then the index's predicate is implied by top-level
361+
* restriction clauses, so we can use it. However, it might also
362+
* be implied by the current OR subclause (perhaps in conjunction
363+
* with the top-level clauses), in which case we can use it for this
364+
* particular scan.
365+
*
366+
* XXX this code is partially redundant with logic in
367+
* group_clauses_by_indexkey_for_or(); consider refactoring.
368+
*/
359369
if (index->indpred != NIL && !index->predOK)
360-
continue;
370+
{
371+
List *subclauserinfos;
372+
373+
if (and_clause((Node *) subclause))
374+
subclauserinfos = list_copy(((BoolExpr *) subclause)->args);
375+
else if (IsA(subclause, RestrictInfo))
376+
subclauserinfos = list_make1(subclause);
377+
else
378+
continue; /* probably can't happen */
379+
if (!pred_test(index->indpred,
380+
list_concat(subclauserinfos,
381+
rel->baserestrictinfo)))
382+
continue;
383+
}
361384

362385
/* Collect index clauses usable with this index */
363386
indexclauses = group_clauses_by_indexkey_for_or(rel, index, subclause);
364387

365-
/* Ignore index if it doesn't match the subclause at all */
366-
if (indexclauses == NIL)
388+
/*
389+
* Ignore index if it doesn't match the subclause at all; except
390+
* that if it's a partial index, consider it anyway, since the
391+
* selectivity of the predicate alone might make the index useful.
392+
*/
393+
if (indexclauses == NIL && index->indpred == NIL)
367394
continue;
368395

369396
/* Convert clauses to indexquals the executor can handle */

src/include/optimizer/paths.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group
99
* Portions Copyright (c) 1994, Regents of the University of California
1010
*
11-
* $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.75 2004/08/29 05:06:57 momjian Exp $
11+
* $PostgreSQL: pgsql/src/include/optimizer/paths.h,v 1.76 2004/10/11 22:57:00 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -44,6 +44,7 @@ extern List *group_clauses_by_indexkey_for_or(RelOptInfo *rel,
4444
extern List *expand_indexqual_conditions(IndexOptInfo *index,
4545
List *clausegroups);
4646
extern void check_partial_indexes(Query *root, RelOptInfo *rel);
47+
extern bool pred_test(List *predicate_list, List *restrictinfo_list);
4748
extern List *flatten_clausegroups_list(List *clausegroups);
4849
extern Expr *make_expr_from_indexclauses(List *indexclauses);
4950

0 commit comments

Comments
 (0)