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

Commit 1455c2e

Browse files
melanieplagemanCommitfest Bot
authored and
Commitfest Bot
committed
BitmapHeapScan uses the read stream API
Make Bitmap Heap Scan use the read stream API instead of invoking ReadBuffer() for each block indicated by the bitmap. The read stream API handles prefetching, so remove all of the explicit prefetching from bitmap heap scan code. Now, heap table AM implements a read stream callback which uses the bitmap iterator to return the next required block to the read stream code. Reviewed-by: Tomas Vondra <tomas@vondra.me> Discussion: https://postgr.es/m/d4bb26c9-fe07-439e-ac53-c0e244387e01%40vondra.me
1 parent f850d35 commit 1455c2e

File tree

5 files changed

+124
-435
lines changed

5 files changed

+124
-435
lines changed

src/backend/access/heap/heapam.c

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,72 @@ heap_scan_stream_read_next_serial(ReadStream *stream,
279279
return scan->rs_prefetch_block;
280280
}
281281

282+
/*
283+
* Read stream API callback for bitmap heap scans.
284+
* Returns the next block the caller wants from the read stream or
285+
* InvalidBlockNumber when done.
286+
*/
287+
static BlockNumber
288+
bitmapheap_stream_read_next(ReadStream *pgsr, void *private_data,
289+
void *per_buffer_data)
290+
{
291+
TBMIterateResult *tbmres = per_buffer_data;
292+
BitmapHeapScanDesc bscan = (BitmapHeapScanDesc) private_data;
293+
HeapScanDesc hscan = (HeapScanDesc) bscan;
294+
TableScanDesc sscan = &hscan->rs_base;
295+
296+
for (;;)
297+
{
298+
CHECK_FOR_INTERRUPTS();
299+
300+
/* no more entries in the bitmap */
301+
if (!tbm_iterate(&sscan->st.rs_tbmiterator, tbmres))
302+
return InvalidBlockNumber;
303+
304+
/*
305+
* Ignore any claimed entries past what we think is the end of the
306+
* relation. It may have been extended after the start of our scan (we
307+
* only hold an AccessShareLock, and it could be inserts from this
308+
* backend). We don't take this optimization in SERIALIZABLE
309+
* isolation though, as we need to examine all invisible tuples
310+
* reachable by the index.
311+
*/
312+
if (!IsolationIsSerializable() &&
313+
tbmres->blockno >= hscan->rs_nblocks)
314+
continue;
315+
316+
/*
317+
* We can skip fetching the heap page if we don't need any fields from
318+
* the heap, the bitmap entries don't need rechecking, and all tuples
319+
* on the page are visible to our transaction.
320+
*/
321+
if (!(sscan->rs_flags & SO_NEED_TUPLES) &&
322+
!tbmres->recheck &&
323+
VM_ALL_VISIBLE(sscan->rs_rd, tbmres->blockno, &bscan->rs_vmbuffer))
324+
{
325+
OffsetNumber offsets[TBM_MAX_TUPLES_PER_PAGE];
326+
int noffsets;
327+
328+
/* can't be lossy in the skip_fetch case */
329+
Assert(!tbmres->lossy);
330+
Assert(bscan->rs_empty_tuples_pending >= 0);
331+
332+
/*
333+
* We throw away the offsets, but this is the easiest way to get a
334+
* count of tuples.
335+
*/
336+
noffsets = tbm_extract_page_tuple(tbmres, offsets, TBM_MAX_TUPLES_PER_PAGE);
337+
bscan->rs_empty_tuples_pending += noffsets;
338+
continue;
339+
}
340+
341+
return tbmres->blockno;
342+
}
343+
344+
/* not reachable */
345+
Assert(false);
346+
}
347+
282348
/* ----------------
283349
* initscan - scan code common to heap_beginscan and heap_rescan
284350
* ----------------
@@ -1067,6 +1133,7 @@ heap_beginscan(Relation relation, Snapshot snapshot,
10671133
scan->rs_base.rs_flags = flags;
10681134
scan->rs_base.rs_parallel = parallel_scan;
10691135
scan->rs_strategy = NULL; /* set in initscan */
1136+
scan->rs_cbuf = InvalidBuffer;
10701137

10711138
/*
10721139
* Disable page-at-a-time mode if it's not a MVCC-safe snapshot.
@@ -1146,6 +1213,16 @@ heap_beginscan(Relation relation, Snapshot snapshot,
11461213
scan,
11471214
0);
11481215
}
1216+
else if (scan->rs_base.rs_flags & SO_TYPE_BITMAPSCAN)
1217+
{
1218+
scan->rs_read_stream = read_stream_begin_relation(READ_STREAM_DEFAULT,
1219+
scan->rs_strategy,
1220+
scan->rs_base.rs_rd,
1221+
MAIN_FORKNUM,
1222+
bitmapheap_stream_read_next,
1223+
scan,
1224+
sizeof(TBMIterateResult));
1225+
}
11491226

11501227

11511228
return (TableScanDesc) scan;
@@ -1180,7 +1257,10 @@ heap_rescan(TableScanDesc sscan, ScanKey key, bool set_params,
11801257
* unpin scan buffers
11811258
*/
11821259
if (BufferIsValid(scan->rs_cbuf))
1260+
{
11831261
ReleaseBuffer(scan->rs_cbuf);
1262+
scan->rs_cbuf = InvalidBuffer;
1263+
}
11841264

11851265
if (scan->rs_base.rs_flags & SO_TYPE_BITMAPSCAN)
11861266
{

src/backend/access/heap/heapam_handler.c

Lines changed: 40 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2117,82 +2117,72 @@ heapam_estimate_rel_size(Relation rel, int32 *attr_widths,
21172117

21182118
static bool
21192119
heapam_scan_bitmap_next_block(TableScanDesc scan,
2120-
BlockNumber *blockno, bool *recheck,
2120+
bool *recheck,
21212121
uint64 *lossy_pages, uint64 *exact_pages)
21222122
{
21232123
BitmapHeapScanDesc bscan = (BitmapHeapScanDesc) scan;
21242124
HeapScanDesc hscan = (HeapScanDesc) bscan;
21252125
BlockNumber block;
2126+
void *per_buffer_data;
21262127
Buffer buffer;
21272128
Snapshot snapshot;
21282129
int ntup;
2129-
TBMIterateResult tbmres;
2130+
TBMIterateResult *tbmres;
21302131
OffsetNumber offsets[TBM_MAX_TUPLES_PER_PAGE];
21312132
int noffsets = -1;
21322133

21332134
Assert(scan->rs_flags & SO_TYPE_BITMAPSCAN);
2135+
Assert(hscan->rs_read_stream);
21342136

21352137
hscan->rs_cindex = 0;
21362138
hscan->rs_ntuples = 0;
21372139

2138-
*blockno = InvalidBlockNumber;
2139-
*recheck = true;
2140-
2141-
do
2140+
/* Release buffer containing previous block. */
2141+
if (BufferIsValid(hscan->rs_cbuf))
21422142
{
2143-
CHECK_FOR_INTERRUPTS();
2143+
ReleaseBuffer(hscan->rs_cbuf);
2144+
hscan->rs_cbuf = InvalidBuffer;
2145+
}
21442146

2145-
if (!tbm_iterate(&scan->st.rs_tbmiterator, &tbmres))
2146-
return false;
2147+
hscan->rs_cbuf = read_stream_next_buffer(hscan->rs_read_stream,
2148+
&per_buffer_data);
21472149

2148-
/* Exact pages need their tuple offsets extracted. */
2149-
if (!tbmres.lossy)
2150-
noffsets = tbm_extract_page_tuple(&tbmres, offsets,
2151-
TBM_MAX_TUPLES_PER_PAGE);
2150+
if (BufferIsInvalid(hscan->rs_cbuf))
2151+
{
2152+
if (BufferIsValid(bscan->rs_vmbuffer))
2153+
{
2154+
ReleaseBuffer(bscan->rs_vmbuffer);
2155+
bscan->rs_vmbuffer = InvalidBuffer;
2156+
}
21522157

21532158
/*
2154-
* Ignore any claimed entries past what we think is the end of the
2155-
* relation. It may have been extended after the start of our scan (we
2156-
* only hold an AccessShareLock, and it could be inserts from this
2157-
* backend). We don't take this optimization in SERIALIZABLE
2158-
* isolation though, as we need to examine all invisible tuples
2159-
* reachable by the index.
2159+
* Bitmap is exhausted. Time to emit empty tuples if relevant. We emit
2160+
* all empty tuples at the end instead of emitting them per block we
2161+
* skip fetching. This is necessary because the streaming read API
2162+
* will only return TBMIterateResults for blocks actually fetched.
2163+
* When we skip fetching a block, we keep track of how many empty
2164+
* tuples to emit at the end of the BitmapHeapScan. We do not recheck
2165+
* all NULL tuples.
21602166
*/
2161-
} while (!IsolationIsSerializable() &&
2162-
tbmres.blockno >= hscan->rs_nblocks);
2167+
*recheck = false;
2168+
return bscan->rs_empty_tuples_pending > 0;
2169+
}
21632170

2164-
/* Got a valid block */
2165-
*blockno = tbmres.blockno;
2166-
*recheck = tbmres.recheck;
2171+
Assert(per_buffer_data);
21672172

2168-
/*
2169-
* We can skip fetching the heap page if we don't need any fields from the
2170-
* heap, the bitmap entries don't need rechecking, and all tuples on the
2171-
* page are visible to our transaction.
2172-
*/
2173-
if (!(scan->rs_flags & SO_NEED_TUPLES) &&
2174-
!tbmres.recheck &&
2175-
VM_ALL_VISIBLE(scan->rs_rd, tbmres.blockno, &bscan->rs_vmbuffer))
2176-
{
2177-
/* can't be lossy in the skip_fetch case */
2178-
Assert(!tbmres.lossy);
2179-
Assert(bscan->rs_empty_tuples_pending >= 0);
2180-
Assert(noffsets > -1);
2173+
tbmres = per_buffer_data;
21812174

2182-
bscan->rs_empty_tuples_pending += noffsets;
2175+
Assert(BlockNumberIsValid(tbmres->blockno));
2176+
Assert(BufferGetBlockNumber(hscan->rs_cbuf) == tbmres->blockno);
21832177

2184-
return true;
2185-
}
2178+
/* Exact pages need their tuple offsets extracted. */
2179+
if (!tbmres->lossy)
2180+
noffsets = tbm_extract_page_tuple(tbmres, offsets,
2181+
TBM_MAX_TUPLES_PER_PAGE);
21862182

2187-
block = tbmres.blockno;
2183+
*recheck = tbmres->recheck;
21882184

2189-
/*
2190-
* Acquire pin on the target heap page, trading in any pin we held before.
2191-
*/
2192-
hscan->rs_cbuf = ReleaseAndReadBuffer(hscan->rs_cbuf,
2193-
scan->rs_rd,
2194-
block);
2195-
hscan->rs_cblock = block;
2185+
block = hscan->rs_cblock = tbmres->blockno;
21962186
buffer = hscan->rs_cbuf;
21972187
snapshot = scan->rs_snapshot;
21982188

@@ -2213,7 +2203,7 @@ heapam_scan_bitmap_next_block(TableScanDesc scan,
22132203
/*
22142204
* We need two separate strategies for lossy and non-lossy cases.
22152205
*/
2216-
if (!tbmres.lossy)
2206+
if (!tbmres->lossy)
22172207
{
22182208
/*
22192209
* Bitmap is non-lossy, so we just look through the offsets listed in
@@ -2277,7 +2267,7 @@ heapam_scan_bitmap_next_block(TableScanDesc scan,
22772267
Assert(ntup <= MaxHeapTuplesPerPage);
22782268
hscan->rs_ntuples = ntup;
22792269

2280-
if (tbmres.lossy)
2270+
if (tbmres->lossy)
22812271
(*lossy_pages)++;
22822272
else
22832273
(*exact_pages)++;

0 commit comments

Comments
 (0)