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

Commit aea916f

Browse files
Fix bitmapheapscan incorrect recheck of NULL tuples
The bitmap heap scan skip fetch optimization skips fetching the heap block when a page is set all-visible in the visibility map and no columns from the table are needed to satisfy the query. 2b73a8c and c395322 changed the control flow of bitmap heap scan to use the read stream API. The read stream API returns buffers containing blocks to the user. To make this work with the skip fetch optimization, we keep a count of the empty tuples we need to emit for all the blocks skipped and only emit the empty tuples after processing the next block fetched from the heap or at the end of the scan. It's incorrect to recheck NULL tuples, so we must set `recheck` to false before yielding control back to BitmapHeapNext(). This was done before emitting any remaining empty tuples at the end of the scan but not for empty tuples emitted during the scan. This meant that if a page fetched from the heap did require recheck and set `recheck` to true and then we emitted empty tuples for subsequent blocks, we would get wrong results. Fix this by always setting `recheck` to false before emitting empty tuples. Reported-by: Alexander Lakhin <exclusion@gmail.com> Tested-by: Andres Freund <andres@anarazel.de> Discussion: https://postgr.es/m/496f7acd-881c-4df3-9bd3-8f8534dfec26%40gmail.com
1 parent 0e3e0ec commit aea916f

File tree

3 files changed

+41
-11
lines changed

3 files changed

+41
-11
lines changed

src/backend/access/heap/heapam_handler.c

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2147,6 +2147,19 @@ heapam_scan_bitmap_next_tuple(TableScanDesc scan,
21472147
*/
21482148
ExecStoreAllNullTuple(slot);
21492149
bscan->rs_empty_tuples_pending--;
2150+
2151+
/*
2152+
* We do not recheck all NULL tuples. Because the streaming read
2153+
* API only yields TBMIterateResults for blocks actually fetched
2154+
* from the heap, we must unset `recheck` ourselves here to ensure
2155+
* correct results.
2156+
*
2157+
* Our read stream callback accrues a count of empty tuples to
2158+
* emit and then emits them after emitting tuples from the next
2159+
* fetched block. If no blocks need fetching, we'll emit the
2160+
* accrued count at the end of the scan.
2161+
*/
2162+
*recheck = false;
21502163
return true;
21512164
}
21522165

@@ -2510,13 +2523,14 @@ BitmapHeapScanNextBlock(TableScanDesc scan,
25102523
}
25112524

25122525
/*
2513-
* Bitmap is exhausted. Time to emit empty tuples if relevant. We emit
2514-
* all empty tuples at the end instead of emitting them per block we
2515-
* skip fetching. This is necessary because the streaming read API
2516-
* will only return TBMIterateResults for blocks actually fetched.
2517-
* When we skip fetching a block, we keep track of how many empty
2518-
* tuples to emit at the end of the BitmapHeapScan. We do not recheck
2519-
* all NULL tuples.
2526+
* The bitmap is exhausted. Now emit any remaining empty tuples. The
2527+
* read stream API only returns TBMIterateResults for blocks actually
2528+
* fetched from the heap. Our callback will accrue a count of empty
2529+
* tuples to emit for all blocks we skipped fetching. So, if we skip
2530+
* fetching heap blocks at the end of the relation (or no heap blocks
2531+
* are fetched) we need to ensure we emit empty tuples before ending
2532+
* the scan. We don't recheck empty tuples so ensure `recheck` is
2533+
* unset.
25202534
*/
25212535
*recheck = false;
25222536
return bscan->rs_empty_tuples_pending > 0;

src/test/regress/expected/bitmapops.out

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
-- there's a maximum number of a,b combinations in the table.
99
-- That allows us to test all the different combinations of
1010
-- lossy and non-lossy pages with the minimum amount of data
11-
CREATE TABLE bmscantest (a int, b int, t text);
11+
CREATE TABLE bmscantest (a int, b int, t text) WITH (autovacuum_enabled = false);
1212
INSERT INTO bmscantest
1313
SELECT (r%53), (r%59), 'foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo'
1414
FROM generate_series(1,70000) r;
@@ -20,7 +20,17 @@ set enable_indexscan=false;
2020
set enable_seqscan=false;
2121
-- Lower work_mem to trigger use of lossy bitmaps
2222
set work_mem = 64;
23-
-- Test bitmap-and.
23+
-- Test bitmap-and without the skip fetch optimization.
24+
SELECT count(*) FROM bmscantest WHERE a = 1 AND b = 1;
25+
count
26+
-------
27+
23
28+
(1 row)
29+
30+
-- Test that we return correct results when using the skip fetch optimization
31+
-- VACUUM FREEZE will set all the pages in the relation all-visible, enabling
32+
-- the optimization.
33+
VACUUM (FREEZE) bmscantest;
2434
SELECT count(*) FROM bmscantest WHERE a = 1 AND b = 1;
2535
count
2636
-------

src/test/regress/sql/bitmapops.sql

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
-- That allows us to test all the different combinations of
1313
-- lossy and non-lossy pages with the minimum amount of data
1414

15-
CREATE TABLE bmscantest (a int, b int, t text);
15+
CREATE TABLE bmscantest (a int, b int, t text) WITH (autovacuum_enabled = false);
1616

1717
INSERT INTO bmscantest
1818
SELECT (r%53), (r%59), 'foooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo'
@@ -29,8 +29,14 @@ set enable_seqscan=false;
2929
-- Lower work_mem to trigger use of lossy bitmaps
3030
set work_mem = 64;
3131

32+
-- Test bitmap-and without the skip fetch optimization.
33+
SELECT count(*) FROM bmscantest WHERE a = 1 AND b = 1;
34+
35+
-- Test that we return correct results when using the skip fetch optimization
36+
-- VACUUM FREEZE will set all the pages in the relation all-visible, enabling
37+
-- the optimization.
38+
VACUUM (FREEZE) bmscantest;
3239

33-
-- Test bitmap-and.
3440
SELECT count(*) FROM bmscantest WHERE a = 1 AND b = 1;
3541

3642
-- Test bitmap-or.

0 commit comments

Comments
 (0)