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

Commit 04e72ed

Browse files
committed
BitmapHeapScan: Push skip_fetch optimization into table AM
Commit 7c70996 introduced an optimization to allow bitmap scans to operate like index-only scans by not fetching a block from the heap if none of the underlying data is needed and the block is marked all visible in the visibility map. With the introduction of table AMs, a FIXME was added to this code indicating that the skip_fetch logic should be pushed into the table AM-specific code, as not all table AMs may use a visibility map in the same way. This commit resolves this FIXME for the current block. The layering violation is still present in BitmapHeapScans's prefetching code, which uses the visibility map to decide whether or not to prefetch a block. However, this can be addressed independently. Author: Melanie Plageman Reviewed-by: Andres Freund, Heikki Linnakangas, Tomas Vondra, Mark Dilger Discussion: https://postgr.es/m/CAAKRu_ZwCwWFeL_H3ia26bP2e7HiKLWt0ZmGXPVwPO6uXq0vaA%40mail.gmail.com
1 parent 87c21bb commit 04e72ed

File tree

6 files changed

+105
-93
lines changed

6 files changed

+105
-93
lines changed

src/backend/access/heap/heapam.c

+15
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,8 @@ heap_beginscan(Relation relation, Snapshot snapshot,
967967
scan->rs_base.rs_flags = flags;
968968
scan->rs_base.rs_parallel = parallel_scan;
969969
scan->rs_strategy = NULL; /* set in initscan */
970+
scan->rs_vmbuffer = InvalidBuffer;
971+
scan->rs_empty_tuples_pending = 0;
970972

971973
/*
972974
* Disable page-at-a-time mode if it's not a MVCC-safe snapshot.
@@ -1055,6 +1057,14 @@ heap_rescan(TableScanDesc sscan, ScanKey key, bool set_params,
10551057
if (BufferIsValid(scan->rs_cbuf))
10561058
ReleaseBuffer(scan->rs_cbuf);
10571059

1060+
if (BufferIsValid(scan->rs_vmbuffer))
1061+
{
1062+
ReleaseBuffer(scan->rs_vmbuffer);
1063+
scan->rs_vmbuffer = InvalidBuffer;
1064+
}
1065+
1066+
Assert(scan->rs_empty_tuples_pending == 0);
1067+
10581068
/*
10591069
* reinitialize scan descriptor
10601070
*/
@@ -1074,6 +1084,11 @@ heap_endscan(TableScanDesc sscan)
10741084
if (BufferIsValid(scan->rs_cbuf))
10751085
ReleaseBuffer(scan->rs_cbuf);
10761086

1087+
if (BufferIsValid(scan->rs_vmbuffer))
1088+
ReleaseBuffer(scan->rs_vmbuffer);
1089+
1090+
Assert(scan->rs_empty_tuples_pending == 0);
1091+
10771092
/*
10781093
* decrement relation reference count and free scan descriptor storage
10791094
*/

src/backend/access/heap/heapam_handler.c

+29
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "access/syncscan.h"
2828
#include "access/tableam.h"
2929
#include "access/tsmapi.h"
30+
#include "access/visibilitymap.h"
3031
#include "access/xact.h"
3132
#include "catalog/catalog.h"
3233
#include "catalog/index.h"
@@ -2198,6 +2199,24 @@ heapam_scan_bitmap_next_block(TableScanDesc scan,
21982199
hscan->rs_cindex = 0;
21992200
hscan->rs_ntuples = 0;
22002201

2202+
/*
2203+
* We can skip fetching the heap page if we don't need any fields from the
2204+
* heap, the bitmap entries don't need rechecking, and all tuples on the
2205+
* page are visible to our transaction.
2206+
*/
2207+
if (!(scan->rs_flags & SO_NEED_TUPLES) &&
2208+
!tbmres->recheck &&
2209+
VM_ALL_VISIBLE(scan->rs_rd, tbmres->blockno, &hscan->rs_vmbuffer))
2210+
{
2211+
/* can't be lossy in the skip_fetch case */
2212+
Assert(tbmres->ntuples >= 0);
2213+
Assert(hscan->rs_empty_tuples_pending >= 0);
2214+
2215+
hscan->rs_empty_tuples_pending += tbmres->ntuples;
2216+
2217+
return true;
2218+
}
2219+
22012220
/*
22022221
* Ignore any claimed entries past what we think is the end of the
22032222
* relation. It may have been extended after the start of our scan (we
@@ -2310,6 +2329,16 @@ heapam_scan_bitmap_next_tuple(TableScanDesc scan,
23102329
Page page;
23112330
ItemId lp;
23122331

2332+
if (hscan->rs_empty_tuples_pending > 0)
2333+
{
2334+
/*
2335+
* If we don't have to fetch the tuple, just return nulls.
2336+
*/
2337+
ExecStoreAllNullTuple(slot);
2338+
hscan->rs_empty_tuples_pending--;
2339+
return true;
2340+
}
2341+
23132342
/*
23142343
* Out of range? If so, nothing more to look at on this page
23152344
*/

src/backend/executor/nodeBitmapHeapscan.c

+39-85
Original file line numberDiff line numberDiff line change
@@ -105,16 +105,6 @@ BitmapHeapNext(BitmapHeapScanState *node)
105105
*/
106106
if (!node->initialized)
107107
{
108-
/*
109-
* We can potentially skip fetching heap pages if we do not need any
110-
* columns of the table, either for checking non-indexable quals or
111-
* for returning data. This test is a bit simplistic, as it checks
112-
* the stronger condition that there's no qual or return tlist at all.
113-
* But in most cases it's probably not worth working harder than that.
114-
*/
115-
node->can_skip_fetch = (node->ss.ps.plan->qual == NIL &&
116-
node->ss.ps.plan->targetlist == NIL);
117-
118108
if (!pstate)
119109
{
120110
tbm = (TIDBitmap *) MultiExecProcNode(outerPlanState(node));
@@ -195,10 +185,24 @@ BitmapHeapNext(BitmapHeapScanState *node)
195185
*/
196186
if (!scan)
197187
{
188+
bool need_tuples = false;
189+
190+
/*
191+
* We can potentially skip fetching heap pages if we do not need
192+
* any columns of the table, either for checking non-indexable
193+
* quals or for returning data. This test is a bit simplistic, as
194+
* it checks the stronger condition that there's no qual or return
195+
* tlist at all. But in most cases it's probably not worth working
196+
* harder than that.
197+
*/
198+
need_tuples = (node->ss.ps.plan->qual != NIL ||
199+
node->ss.ps.plan->targetlist != NIL);
200+
198201
scan = table_beginscan_bm(node->ss.ss_currentRelation,
199202
node->ss.ps.state->es_snapshot,
200203
0,
201-
NULL);
204+
NULL,
205+
need_tuples);
202206

203207
node->ss.ss_currentScanDesc = scan;
204208
}
@@ -208,7 +212,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
208212

209213
for (;;)
210214
{
211-
bool skip_fetch;
215+
bool valid_block;
212216

213217
CHECK_FOR_INTERRUPTS();
214218

@@ -229,37 +233,14 @@ BitmapHeapNext(BitmapHeapScanState *node)
229233

230234
BitmapAdjustPrefetchIterator(node, tbmres);
231235

236+
valid_block = table_scan_bitmap_next_block(scan, tbmres);
237+
232238
if (tbmres->ntuples >= 0)
233239
node->exact_pages++;
234240
else
235241
node->lossy_pages++;
236242

237-
/*
238-
* We can skip fetching the heap page if we don't need any fields
239-
* from the heap, and the bitmap entries don't need rechecking,
240-
* and all tuples on the page are visible to our transaction.
241-
*
242-
* XXX: It's a layering violation that we do these checks above
243-
* tableam, they should probably moved below it at some point.
244-
*/
245-
skip_fetch = (node->can_skip_fetch &&
246-
!tbmres->recheck &&
247-
VM_ALL_VISIBLE(node->ss.ss_currentRelation,
248-
tbmres->blockno,
249-
&node->vmbuffer));
250-
251-
if (skip_fetch)
252-
{
253-
/* can't be lossy in the skip_fetch case */
254-
Assert(tbmres->ntuples >= 0);
255-
256-
/*
257-
* The number of tuples on this page is put into
258-
* node->return_empty_tuples.
259-
*/
260-
node->return_empty_tuples = tbmres->ntuples;
261-
}
262-
else if (!table_scan_bitmap_next_block(scan, tbmres))
243+
if (!valid_block)
263244
{
264245
/* AM doesn't think this block is valid, skip */
265246
continue;
@@ -302,52 +283,33 @@ BitmapHeapNext(BitmapHeapScanState *node)
302283
* should happen only when we have determined there is still something
303284
* to do on the current page, else we may uselessly prefetch the same
304285
* page we are just about to request for real.
305-
*
306-
* XXX: It's a layering violation that we do these checks above
307-
* tableam, they should probably moved below it at some point.
308286
*/
309287
BitmapPrefetch(node, scan);
310288

311-
if (node->return_empty_tuples > 0)
289+
/*
290+
* Attempt to fetch tuple from AM.
291+
*/
292+
if (!table_scan_bitmap_next_tuple(scan, tbmres, slot))
312293
{
313-
/*
314-
* If we don't have to fetch the tuple, just return nulls.
315-
*/
316-
ExecStoreAllNullTuple(slot);
317-
318-
if (--node->return_empty_tuples == 0)
319-
{
320-
/* no more tuples to return in the next round */
321-
node->tbmres = tbmres = NULL;
322-
}
294+
/* nothing more to look at on this page */
295+
node->tbmres = tbmres = NULL;
296+
continue;
323297
}
324-
else
298+
299+
/*
300+
* If we are using lossy info, we have to recheck the qual conditions
301+
* at every tuple.
302+
*/
303+
if (tbmres->recheck)
325304
{
326-
/*
327-
* Attempt to fetch tuple from AM.
328-
*/
329-
if (!table_scan_bitmap_next_tuple(scan, tbmres, slot))
305+
econtext->ecxt_scantuple = slot;
306+
if (!ExecQualAndReset(node->bitmapqualorig, econtext))
330307
{
331-
/* nothing more to look at on this page */
332-
node->tbmres = tbmres = NULL;
308+
/* Fails recheck, so drop it and loop back for another */
309+
InstrCountFiltered2(node, 1);
310+
ExecClearTuple(slot);
333311
continue;
334312
}
335-
336-
/*
337-
* If we are using lossy info, we have to recheck the qual
338-
* conditions at every tuple.
339-
*/
340-
if (tbmres->recheck)
341-
{
342-
econtext->ecxt_scantuple = slot;
343-
if (!ExecQualAndReset(node->bitmapqualorig, econtext))
344-
{
345-
/* Fails recheck, so drop it and loop back for another */
346-
InstrCountFiltered2(node, 1);
347-
ExecClearTuple(slot);
348-
continue;
349-
}
350-
}
351313
}
352314

353315
/* OK to return this tuple */
@@ -519,7 +481,7 @@ BitmapPrefetch(BitmapHeapScanState *node, TableScanDesc scan)
519481
* it did for the current heap page; which is not a certainty
520482
* but is true in many cases.
521483
*/
522-
skip_fetch = (node->can_skip_fetch &&
484+
skip_fetch = (!(scan->rs_flags & SO_NEED_TUPLES) &&
523485
(node->tbmres ? !node->tbmres->recheck : false) &&
524486
VM_ALL_VISIBLE(node->ss.ss_currentRelation,
525487
tbmpre->blockno,
@@ -570,7 +532,7 @@ BitmapPrefetch(BitmapHeapScanState *node, TableScanDesc scan)
570532
}
571533

572534
/* As above, skip prefetch if we expect not to need page */
573-
skip_fetch = (node->can_skip_fetch &&
535+
skip_fetch = (!(scan->rs_flags & SO_NEED_TUPLES) &&
574536
(node->tbmres ? !node->tbmres->recheck : false) &&
575537
VM_ALL_VISIBLE(node->ss.ss_currentRelation,
576538
tbmpre->blockno,
@@ -640,8 +602,6 @@ ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
640602
tbm_end_shared_iterate(node->shared_prefetch_iterator);
641603
if (node->tbm)
642604
tbm_free(node->tbm);
643-
if (node->vmbuffer != InvalidBuffer)
644-
ReleaseBuffer(node->vmbuffer);
645605
if (node->pvmbuffer != InvalidBuffer)
646606
ReleaseBuffer(node->pvmbuffer);
647607
node->tbm = NULL;
@@ -651,7 +611,6 @@ ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
651611
node->initialized = false;
652612
node->shared_tbmiterator = NULL;
653613
node->shared_prefetch_iterator = NULL;
654-
node->vmbuffer = InvalidBuffer;
655614
node->pvmbuffer = InvalidBuffer;
656615

657616
ExecScanReScan(&node->ss);
@@ -696,8 +655,6 @@ ExecEndBitmapHeapScan(BitmapHeapScanState *node)
696655
tbm_end_shared_iterate(node->shared_tbmiterator);
697656
if (node->shared_prefetch_iterator)
698657
tbm_end_shared_iterate(node->shared_prefetch_iterator);
699-
if (node->vmbuffer != InvalidBuffer)
700-
ReleaseBuffer(node->vmbuffer);
701658
if (node->pvmbuffer != InvalidBuffer)
702659
ReleaseBuffer(node->pvmbuffer);
703660

@@ -741,8 +698,6 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
741698
scanstate->tbm = NULL;
742699
scanstate->tbmiterator = NULL;
743700
scanstate->tbmres = NULL;
744-
scanstate->return_empty_tuples = 0;
745-
scanstate->vmbuffer = InvalidBuffer;
746701
scanstate->pvmbuffer = InvalidBuffer;
747702
scanstate->exact_pages = 0;
748703
scanstate->lossy_pages = 0;
@@ -753,7 +708,6 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
753708
scanstate->shared_tbmiterator = NULL;
754709
scanstate->shared_prefetch_iterator = NULL;
755710
scanstate->pstate = NULL;
756-
scanstate->can_skip_fetch = false;
757711

758712
/*
759713
* Miscellaneous initialization

src/include/access/heapam.h

+10
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,16 @@ typedef struct HeapScanDescData
7676
*/
7777
ParallelBlockTableScanWorkerData *rs_parallelworkerdata;
7878

79+
/*
80+
* These fields are only used for bitmap scans for the "skip fetch"
81+
* optimization. Bitmap scans needing no fields from the heap may skip
82+
* fetching an all visible block, instead using the number of tuples per
83+
* block reported by the bitmap to determine how many NULL-filled tuples
84+
* to return.
85+
*/
86+
Buffer rs_vmbuffer;
87+
int rs_empty_tuples_pending;
88+
7989
/* these fields only used in page-at-a-time mode and for bitmap scans */
8090
int rs_cindex; /* current tuple's index in vistuples */
8191
int rs_ntuples; /* number of visible tuples on page */

src/include/access/tableam.h

+11-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ typedef enum ScanOptions
6363

6464
/* unregister snapshot at scan end? */
6565
SO_TEMP_SNAPSHOT = 1 << 9,
66+
67+
/*
68+
* At the discretion of the table AM, bitmap table scans may be able to
69+
* skip fetching a block from the table if none of the table data is
70+
* needed. If table data may be needed, set SO_NEED_TUPLES.
71+
*/
72+
SO_NEED_TUPLES = 1 << 10,
6673
} ScanOptions;
6774

6875
/*
@@ -937,10 +944,13 @@ table_beginscan_strat(Relation rel, Snapshot snapshot,
937944
*/
938945
static inline TableScanDesc
939946
table_beginscan_bm(Relation rel, Snapshot snapshot,
940-
int nkeys, struct ScanKeyData *key)
947+
int nkeys, struct ScanKeyData *key, bool need_tuple)
941948
{
942949
uint32 flags = SO_TYPE_BITMAPSCAN | SO_ALLOW_PAGEMODE;
943950

951+
if (need_tuple)
952+
flags |= SO_NEED_TUPLES;
953+
944954
return rel->rd_tableam->scan_begin(rel, snapshot, nkeys, key, NULL, flags);
945955
}
946956

src/include/nodes/execnodes.h

+1-7
Original file line numberDiff line numberDiff line change
@@ -1794,10 +1794,7 @@ typedef struct ParallelBitmapHeapState
17941794
* tbm bitmap obtained from child index scan(s)
17951795
* tbmiterator iterator for scanning current pages
17961796
* tbmres current-page data
1797-
* can_skip_fetch can we potentially skip tuple fetches in this scan?
1798-
* return_empty_tuples number of empty tuples to return
1799-
* vmbuffer buffer for visibility-map lookups
1800-
* pvmbuffer ditto, for prefetched pages
1797+
* pvmbuffer buffer for visibility-map lookups of prefetched pages
18011798
* exact_pages total number of exact pages retrieved
18021799
* lossy_pages total number of lossy pages retrieved
18031800
* prefetch_iterator iterator for prefetching ahead of current page
@@ -1817,9 +1814,6 @@ typedef struct BitmapHeapScanState
18171814
TIDBitmap *tbm;
18181815
TBMIterator *tbmiterator;
18191816
TBMIterateResult *tbmres;
1820-
bool can_skip_fetch;
1821-
int return_empty_tuples;
1822-
Buffer vmbuffer;
18231817
Buffer pvmbuffer;
18241818
long exact_pages;
18251819
long lossy_pages;

0 commit comments

Comments
 (0)