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

Commit a4ca842

Browse files
committed
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.
1 parent 3535cb8 commit a4ca842

File tree

9 files changed

+399
-83
lines changed

9 files changed

+399
-83
lines changed

src/backend/nodes/list.c

Lines changed: 155 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
*
1010
*
1111
* IDENTIFICATION
12-
* $PostgreSQL: pgsql/src/backend/nodes/list.c,v 1.64 2005/03/17 05:47:01 neilc Exp $
12+
* $PostgreSQL: pgsql/src/backend/nodes/list.c,v 1.65 2005/07/28 20:26:19 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -686,9 +686,13 @@ list_delete_first(List *list)
686686
* The returned list is newly-allocated, although the content of the
687687
* cells is the same (i.e. any pointed-to objects are not copied).
688688
*
689-
* NB: Bizarrely, this function will NOT remove any duplicates that
690-
* are present in list1 (so it is not really a union at all!). Also,
691-
* this function could probably be implemented a lot faster if it is a
689+
* NB: this function will NOT remove any duplicates that are present
690+
* in list1 (so it only performs a "union" if list1 is known unique to
691+
* start with). Also, if you are about to write "x = list_union(x, y)"
692+
* you probably want to use list_concat_unique() instead to avoid wasting
693+
* the list cells of the old x list.
694+
*
695+
* This function could probably be implemented a lot faster if it is a
692696
* performance bottleneck.
693697
*/
694698
List *
@@ -888,7 +892,153 @@ list_difference_oid(List *list1, List *list2)
888892
return result;
889893
}
890894

891-
/* Free all storage in a list, and optionally the pointed-to elements */
895+
/*
896+
* Append datum to list, but only if it isn't already in the list.
897+
*
898+
* Whether an element is already a member of the list is determined
899+
* via equal().
900+
*/
901+
List *
902+
list_append_unique(List *list, void *datum)
903+
{
904+
if (list_member(list, datum))
905+
return list;
906+
else
907+
return lappend(list, datum);
908+
}
909+
910+
/*
911+
* This variant of list_append_unique() determines list membership via
912+
* simple pointer equality.
913+
*/
914+
List *
915+
list_append_unique_ptr(List *list, void *datum)
916+
{
917+
if (list_member_ptr(list, datum))
918+
return list;
919+
else
920+
return lappend(list, datum);
921+
}
922+
923+
/*
924+
* This variant of list_append_unique() operates upon lists of integers.
925+
*/
926+
List *
927+
list_append_unique_int(List *list, int datum)
928+
{
929+
if (list_member_int(list, datum))
930+
return list;
931+
else
932+
return lappend_int(list, datum);
933+
}
934+
935+
/*
936+
* This variant of list_append_unique() operates upon lists of OIDs.
937+
*/
938+
List *
939+
list_append_unique_oid(List *list, Oid datum)
940+
{
941+
if (list_member_oid(list, datum))
942+
return list;
943+
else
944+
return lappend_oid(list, datum);
945+
}
946+
947+
/*
948+
* Append to list1 each member of list2 that isn't already in list1.
949+
*
950+
* Whether an element is already a member of the list is determined
951+
* via equal().
952+
*
953+
* This is almost the same functionality as list_union(), but list1 is
954+
* modified in-place rather than being copied. Note also that list2's cells
955+
* are not inserted in list1, so the analogy to list_concat() isn't perfect.
956+
*/
957+
List *
958+
list_concat_unique(List *list1, List *list2)
959+
{
960+
ListCell *cell;
961+
962+
Assert(IsPointerList(list1));
963+
Assert(IsPointerList(list2));
964+
965+
foreach(cell, list2)
966+
{
967+
if (!list_member(list1, lfirst(cell)))
968+
list1 = lappend(list1, lfirst(cell));
969+
}
970+
971+
check_list_invariants(list1);
972+
return list1;
973+
}
974+
975+
/*
976+
* This variant of list_concat_unique() determines list membership via
977+
* simple pointer equality.
978+
*/
979+
List *
980+
list_concat_unique_ptr(List *list1, List *list2)
981+
{
982+
ListCell *cell;
983+
984+
Assert(IsPointerList(list1));
985+
Assert(IsPointerList(list2));
986+
987+
foreach(cell, list2)
988+
{
989+
if (!list_member_ptr(list1, lfirst(cell)))
990+
list1 = lappend(list1, lfirst(cell));
991+
}
992+
993+
check_list_invariants(list1);
994+
return list1;
995+
}
996+
997+
/*
998+
* This variant of list_concat_unique() operates upon lists of integers.
999+
*/
1000+
List *
1001+
list_concat_unique_int(List *list1, List *list2)
1002+
{
1003+
ListCell *cell;
1004+
1005+
Assert(IsIntegerList(list1));
1006+
Assert(IsIntegerList(list2));
1007+
1008+
foreach(cell, list2)
1009+
{
1010+
if (!list_member_int(list1, lfirst_int(cell)))
1011+
list1 = lappend_int(list1, lfirst_int(cell));
1012+
}
1013+
1014+
check_list_invariants(list1);
1015+
return list1;
1016+
}
1017+
1018+
/*
1019+
* This variant of list_concat_unique() operates upon lists of OIDs.
1020+
*/
1021+
List *
1022+
list_concat_unique_oid(List *list1, List *list2)
1023+
{
1024+
ListCell *cell;
1025+
1026+
Assert(IsOidList(list1));
1027+
Assert(IsOidList(list2));
1028+
1029+
foreach(cell, list2)
1030+
{
1031+
if (!list_member_oid(list1, lfirst_oid(cell)))
1032+
list1 = lappend_oid(list1, lfirst_oid(cell));
1033+
}
1034+
1035+
check_list_invariants(list1);
1036+
return list1;
1037+
}
1038+
1039+
/*
1040+
* Free all storage in a list, and optionally the pointed-to elements
1041+
*/
8921042
static void
8931043
list_free_private(List *list, bool deep)
8941044
{

src/backend/optimizer/path/indxpath.c

Lines changed: 73 additions & 37 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.186 2005/07/02 23:00:40 tgl Exp $
12+
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.187 2005/07/28 20:26:20 tgl Exp $
1313
*
1414
*-------------------------------------------------------------------------
1515
*/
@@ -212,6 +212,8 @@ create_index_paths(PlannerInfo *root, RelOptInfo *rel)
212212
* subclause to any index on x alone, because such a Path would already have
213213
* been generated at the upper level. So we could use an index on x,y,z
214214
* or an index on x,y for the OR subclause, but not an index on just x.
215+
* When dealing with a partial index, a match of the index predicate to
216+
* one of the "current" clauses also makes the index usable.
215217
*
216218
* If istoplevel is true (indicating we are considering the top level of a
217219
* rel's restriction clauses), we will include indexes in the result that
@@ -248,6 +250,8 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
248250
List *restrictclauses;
249251
List *index_pathkeys;
250252
List *useful_pathkeys;
253+
bool useful_predicate;
254+
bool found_clause;
251255
bool index_is_ordered;
252256

253257
/*
@@ -258,29 +262,60 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
258262
* Otherwise, we have to test whether the added clauses are
259263
* sufficient to imply the predicate. If so, we could use
260264
* the index in the current context.
265+
*
266+
* We set useful_predicate to true iff the predicate was proven
267+
* using the current set of clauses. This is needed to prevent
268+
* matching a predOK index to an arm of an OR, which would be
269+
* a legal but pointlessly inefficient plan. (A better plan will
270+
* be generated by just scanning the predOK index alone, no OR.)
261271
*/
262-
if (index->indpred != NIL && !index->predOK)
272+
useful_predicate = false;
273+
if (index->indpred != NIL)
263274
{
264-
if (istoplevel)
265-
continue; /* no point in trying to prove it */
275+
if (index->predOK)
276+
{
277+
if (istoplevel)
278+
{
279+
/* we know predicate was proven from these clauses */
280+
useful_predicate = true;
281+
}
282+
}
283+
else
284+
{
285+
if (istoplevel)
286+
continue; /* no point in trying to prove it */
266287

267-
/* Form all_clauses if not done already */
268-
if (all_clauses == NIL)
269-
all_clauses = list_concat(list_copy(clauses),
270-
outer_clauses);
288+
/* Form all_clauses if not done already */
289+
if (all_clauses == NIL)
290+
all_clauses = list_concat(list_copy(clauses),
291+
outer_clauses);
271292

272-
if (!predicate_implied_by(index->indpred, all_clauses) ||
273-
predicate_implied_by(index->indpred, outer_clauses))
274-
continue;
293+
if (!predicate_implied_by(index->indpred, all_clauses))
294+
continue; /* can't use it at all */
295+
296+
if (!predicate_implied_by(index->indpred, outer_clauses))
297+
useful_predicate = true;
298+
}
275299
}
276300

277301
/*
278302
* 1. Match the index against the available restriction clauses.
303+
* found_clause is set true only if at least one of the current
304+
* clauses was used.
279305
*/
280306
restrictclauses = group_clauses_by_indexkey(index,
281307
clauses,
282308
outer_clauses,
283-
outer_relids);
309+
outer_relids,
310+
&found_clause);
311+
312+
/*
313+
* Not all index AMs support scans with no restriction clauses.
314+
* We can't generate a scan over an index with amoptionalkey = false
315+
* unless there's at least one restriction clause.
316+
*/
317+
if (restrictclauses == NIL && !index->amoptionalkey)
318+
continue;
284319

285320
/*
286321
* 2. Compute pathkeys describing index's ordering, if any, then
@@ -300,20 +335,12 @@ find_usable_indexes(PlannerInfo *root, RelOptInfo *rel,
300335

301336
/*
302337
* 3. Generate an indexscan path if there are relevant restriction
303-
* clauses OR the index ordering is potentially useful for later
304-
* merging or final output ordering.
305-
*
306-
* If there is a predicate, consider it anyway since the index
307-
* predicate has already been found to match the query. The
308-
* selectivity of the predicate might alone make the index useful.
309-
*
310-
* Note: not all index AMs support scans with no restriction clauses.
311-
* We can't generate a scan over an index with amoptionalkey = false
312-
* unless there's at least one restriction clause.
338+
* clauses in the current clauses, OR the index ordering is
339+
* potentially useful for later merging or final output ordering,
340+
* OR the index has a predicate that was proven by the current
341+
* clauses.
313342
*/
314-
if (restrictclauses != NIL ||
315-
(index->amoptionalkey &&
316-
(useful_pathkeys != NIL || index->indpred != NIL)))
343+
if (found_clause || useful_pathkeys != NIL || useful_predicate)
317344
{
318345
ipath = create_index_path(root, index,
319346
restrictclauses,
@@ -627,10 +654,9 @@ bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths)
627654
* with one index key (in no particular order); the top list is ordered by
628655
* index key. (This is depended on by expand_indexqual_conditions().)
629656
*
630-
* As explained in the comments for find_usable_indexes(), we can use
631-
* clauses from either of the given lists, but the result is required to
632-
* use at least one clause from the "current clauses" list. We return
633-
* NIL if we don't find any such clause.
657+
* We can use clauses from either the current clauses or outer_clauses lists,
658+
* but *found_clause is set TRUE only if we used at least one clause from
659+
* the "current clauses" list. See find_usable_indexes() for motivation.
634660
*
635661
* outer_relids determines what Vars will be allowed on the other side
636662
* of a possible index qual; see match_clause_to_indexcol().
@@ -643,18 +669,25 @@ bitmap_and_cost_est(PlannerInfo *root, RelOptInfo *rel, List *paths)
643669
* Example: given an index on (A,B,C), we would return ((C1 C2) () (C3 C4))
644670
* if we find that clauses C1 and C2 use column A, clauses C3 and C4 use
645671
* column C, and no clauses use column B.
672+
*
673+
* Note: in some circumstances we may find the same RestrictInfos coming
674+
* from multiple places. Defend against redundant outputs by using
675+
* list_append_unique_ptr (pointer equality should be good enough).
646676
*/
647677
List *
648678
group_clauses_by_indexkey(IndexOptInfo *index,
649679
List *clauses, List *outer_clauses,
650-
Relids outer_relids)
680+
Relids outer_relids,
681+
bool *found_clause)
651682
{
652683
List *clausegroup_list = NIL;
653-
bool found_clause = false;
684+
bool found_outer_clause = false;
654685
int indexcol = 0;
655686
Oid *classes = index->classlist;
656687

657-
if (clauses == NIL)
688+
*found_clause = false; /* default result */
689+
690+
if (clauses == NIL && outer_clauses == NIL)
658691
return NIL; /* cannot succeed */
659692

660693
do
@@ -675,8 +708,8 @@ group_clauses_by_indexkey(IndexOptInfo *index,
675708
rinfo,
676709
outer_relids))
677710
{
678-
clausegroup = lappend(clausegroup, rinfo);
679-
found_clause = true;
711+
clausegroup = list_append_unique_ptr(clausegroup, rinfo);
712+
*found_clause = true;
680713
}
681714
}
682715

@@ -691,7 +724,10 @@ group_clauses_by_indexkey(IndexOptInfo *index,
691724
curClass,
692725
rinfo,
693726
outer_relids))
694-
clausegroup = lappend(clausegroup, rinfo);
727+
{
728+
clausegroup = list_append_unique_ptr(clausegroup, rinfo);
729+
found_outer_clause = true;
730+
}
695731
}
696732

697733
/*
@@ -707,8 +743,8 @@ group_clauses_by_indexkey(IndexOptInfo *index,
707743

708744
} while (!DoneMatchingIndexKeys(classes));
709745

710-
if (!found_clause)
711-
return NIL;
746+
if (!*found_clause && !found_outer_clause)
747+
return NIL; /* no indexable clauses anywhere */
712748

713749
return clausegroup_list;
714750
}

0 commit comments

Comments
 (0)