diff --git a/src/backend/access/gin/ginget.c b/src/backend/access/gin/ginget.c index b9de54a717..fec3a8fb9d 100644 --- a/src/backend/access/gin/ginget.c +++ b/src/backend/access/gin/ginget.c @@ -801,9 +801,8 @@ entryGetItem(GinState *ginstate, GinScanEntry entry, /* A bitmap result */ BlockNumber advancePastBlk = GinItemPointerGetBlockNumber(&advancePast); OffsetNumber advancePastOff = GinItemPointerGetOffsetNumber(&advancePast); - bool gotitem = false; - do + for (;;) { /* * If we've exhausted all items on this block, move to next block @@ -852,7 +851,6 @@ entryGetItem(GinState *ginstate, GinScanEntry entry, * estimate number of results on this page to support correct * reducing of result even if it's enabled. */ - gotitem = true; break; } @@ -865,7 +863,7 @@ entryGetItem(GinState *ginstate, GinScanEntry entry, /* * First, do a quick check against the last offset on the * page. If that's > advancePast, so are all the other - * offsets. + * offsets, so just go back to the top to get the next page. */ if (entry->matchResult->offsets[entry->matchResult->ntuples - 1] <= advancePastOff) { @@ -882,8 +880,11 @@ entryGetItem(GinState *ginstate, GinScanEntry entry, entry->matchResult->blockno, entry->matchResult->offsets[entry->offset]); entry->offset++; - gotitem = true; - } while (!gotitem || (entry->reduceResult == true && dropItem(entry))); + + /* Done unless we need to reduce the result */ + if (!entry->reduceResult || !dropItem(entry)) + break; + } } else if (!BufferIsValid(entry->buffer)) { @@ -891,7 +892,7 @@ entryGetItem(GinState *ginstate, GinScanEntry entry, * A posting list from an entry tuple, or the last page of a posting * tree. */ - do + for (;;) { if (entry->offset >= entry->nlist) { @@ -901,13 +902,20 @@ entryGetItem(GinState *ginstate, GinScanEntry entry, } entry->curItem = entry->list[entry->offset++]; - } while (ginCompareItemPointers(&entry->curItem, &advancePast) <= 0); - /* XXX: shouldn't we apply the fuzzy search limit here? */ + + /* If we're not past advancePast, keep scanning */ + if (ginCompareItemPointers(&entry->curItem, &advancePast) <= 0) + continue; + + /* Done unless we need to reduce the result */ + if (!entry->reduceResult || !dropItem(entry)) + break; + } } else { /* A posting tree */ - do + for (;;) { /* If we've processed the current batch, load more items */ while (entry->offset >= entry->nlist) @@ -923,8 +931,20 @@ entryGetItem(GinState *ginstate, GinScanEntry entry, entry->curItem = entry->list[entry->offset++]; - } while (ginCompareItemPointers(&entry->curItem, &advancePast) <= 0 || - (entry->reduceResult == true && dropItem(entry))); + /* If we're not past advancePast, keep scanning */ + if (ginCompareItemPointers(&entry->curItem, &advancePast) <= 0) + continue; + + /* Done unless we need to reduce the result */ + if (!entry->reduceResult || !dropItem(entry)) + break; + + /* + * Advance advancePast (so that entryLoadMoreItems will load the + * right data), and keep scanning + */ + advancePast = entry->curItem; + } } } diff --git a/src/test/regress/expected/gin.out b/src/test/regress/expected/gin.out index a3911a6c6c..e26a961bbd 100644 --- a/src/test/regress/expected/gin.out +++ b/src/test/regress/expected/gin.out @@ -1,7 +1,7 @@ -- -- Test GIN indexes. -- --- There are other tests to test different GIN opclassed. This is for testing +-- There are other tests to test different GIN opclasses. This is for testing -- GIN itself. -- Create and populate a test table with a GIN index. create table gin_test_tbl(i int4[]) with (autovacuum_enabled = off); @@ -35,3 +35,41 @@ insert into gin_test_tbl select array[1, 2, g] from generate_series(1, 1000) g; insert into gin_test_tbl select array[1, 3, g] from generate_series(1, 1000) g; delete from gin_test_tbl where i @> array[2]; vacuum gin_test_tbl; +-- Test for "rare && frequent" searches +explain (costs off) +select count(*) from gin_test_tbl where i @> array[1, 999]; + QUERY PLAN +------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on gin_test_tbl + Recheck Cond: (i @> '{1,999}'::integer[]) + -> Bitmap Index Scan on gin_test_idx + Index Cond: (i @> '{1,999}'::integer[]) +(5 rows) + +select count(*) from gin_test_tbl where i @> array[1, 999]; + count +------- + 3 +(1 row) + +-- Very weak test for gin_fuzzy_search_limit +set gin_fuzzy_search_limit = 1000; +explain (costs off) +select count(*) > 0 as ok from gin_test_tbl where i @> array[1]; + QUERY PLAN +--------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on gin_test_tbl + Recheck Cond: (i @> '{1}'::integer[]) + -> Bitmap Index Scan on gin_test_idx + Index Cond: (i @> '{1}'::integer[]) +(5 rows) + +select count(*) > 0 as ok from gin_test_tbl where i @> array[1]; + ok +---- + t +(1 row) + +reset gin_fuzzy_search_limit; diff --git a/src/test/regress/sql/gin.sql b/src/test/regress/sql/gin.sql index c566e9b58c..ef8b63d4c5 100644 --- a/src/test/regress/sql/gin.sql +++ b/src/test/regress/sql/gin.sql @@ -1,7 +1,7 @@ -- -- Test GIN indexes. -- --- There are other tests to test different GIN opclassed. This is for testing +-- There are other tests to test different GIN opclasses. This is for testing -- GIN itself. -- Create and populate a test table with a GIN index. @@ -34,3 +34,19 @@ insert into gin_test_tbl select array[1, 3, g] from generate_series(1, 1000) g; delete from gin_test_tbl where i @> array[2]; vacuum gin_test_tbl; + +-- Test for "rare && frequent" searches +explain (costs off) +select count(*) from gin_test_tbl where i @> array[1, 999]; + +select count(*) from gin_test_tbl where i @> array[1, 999]; + +-- Very weak test for gin_fuzzy_search_limit +set gin_fuzzy_search_limit = 1000; + +explain (costs off) +select count(*) > 0 as ok from gin_test_tbl where i @> array[1]; + +select count(*) > 0 as ok from gin_test_tbl where i @> array[1]; + +reset gin_fuzzy_search_limit;