Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commita4ca842

Browse files
committed
Fix a bunch of bad interactions between partial indexes and the new
planning logic for bitmap indexscans. Partial indexes create cornercases 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 littletenser about eliminating redundant clauses in the generated plan.Per report from Dmitry Karasik.
1 parent3535cb8 commita4ca842

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
returnresult;
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+
returnlist;
906+
else
907+
returnlappend(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+
returnlist;
919+
else
920+
returnlappend(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,intdatum)
928+
{
929+
if (list_member_int(list,datum))
930+
returnlist;
931+
else
932+
returnlappend_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,Oiddatum)
940+
{
941+
if (list_member_oid(list,datum))
942+
returnlist;
943+
else
944+
returnlappend_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+
returnlist1;
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+
returnlist1;
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+
returnlist1;
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+
returnlist1;
1037+
}
1038+
1039+
/*
1040+
* Free all storage in a list, and optionally the pointed-to elements
1041+
*/
8921042
staticvoid
8931043
list_free_private(List*list,booldeep)
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+
booluseful_predicate;
254+
boolfound_clause;
251255
boolindex_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-
Relidsouter_relids)
680+
Relidsouter_relids,
681+
bool*found_clause)
651682
{
652683
List*clausegroup_list=NIL;
653-
boolfound_clause= false;
684+
boolfound_outer_clause= false;
654685
intindexcol=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
returnNIL;/* 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-
returnNIL;
746+
if (!*found_clause&& !found_outer_clause)
747+
returnNIL;/* no indexable clauses anywhere */
712748

713749
returnclausegroup_list;
714750
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp