Skip to content

Commit 6a780e5

Browse files
MMeentCommitfest Bot
authored and
Commitfest Bot
committed
Remove HeapBitmapScan's skip_fetch optimization
The optimization doesn't take concurrent vacuum's removal of TIDs into account, which can remove dead TIDs and make ALL_VISIBLE pages for which we have pointers to those dead TIDs in the bitmap. The optimization itself isn't impossible, but would require more work figuring out that vacuum started removing TIDs and then stop using the optimization. However, we currently don't have such a system in place, making the optimization unsound to keep around. Reported-By: Konstantin Knizhnik <[email protected]> Backpatch-through: 13
1 parent ea3f9b6 commit 6a780e5

File tree

5 files changed

+13
-132
lines changed

5 files changed

+13
-132
lines changed

src/backend/access/heap/heapam.c

+8-54
Original file line numberDiff line numberDiff line change
@@ -314,31 +314,6 @@ bitmapheap_stream_read_next(ReadStream *pgsr, void *private_data,
314314
tbmres->blockno >= hscan->rs_nblocks)
315315
continue;
316316

317-
/*
318-
* We can skip fetching the heap page if we don't need any fields from
319-
* the heap, the bitmap entries don't need rechecking, and all tuples
320-
* on the page are visible to our transaction.
321-
*/
322-
if (!(sscan->rs_flags & SO_NEED_TUPLES) &&
323-
!tbmres->recheck &&
324-
VM_ALL_VISIBLE(sscan->rs_rd, tbmres->blockno, &bscan->rs_vmbuffer))
325-
{
326-
OffsetNumber offsets[TBM_MAX_TUPLES_PER_PAGE];
327-
int noffsets;
328-
329-
/* can't be lossy in the skip_fetch case */
330-
Assert(!tbmres->lossy);
331-
Assert(bscan->rs_empty_tuples_pending >= 0);
332-
333-
/*
334-
* We throw away the offsets, but this is the easiest way to get a
335-
* count of tuples.
336-
*/
337-
noffsets = tbm_extract_page_tuple(tbmres, offsets, TBM_MAX_TUPLES_PER_PAGE);
338-
bscan->rs_empty_tuples_pending += noffsets;
339-
continue;
340-
}
341-
342317
return tbmres->blockno;
343318
}
344319

@@ -1123,9 +1098,10 @@ heap_beginscan(Relation relation, Snapshot snapshot,
11231098
if (flags & SO_TYPE_BITMAPSCAN)
11241099
{
11251100
BitmapHeapScanDesc bscan = palloc(sizeof(BitmapHeapScanDescData));
1126-
1127-
bscan->rs_vmbuffer = InvalidBuffer;
1128-
bscan->rs_empty_tuples_pending = 0;
1101+
/*
1102+
* Bitmap Heap scans do not have any new fields that a normal Heap
1103+
* Scan does not have, so no special initializations required here.
1104+
*/
11291105
scan = (HeapScanDesc) bscan;
11301106
}
11311107
else
@@ -1280,23 +1256,10 @@ heap_rescan(TableScanDesc sscan, ScanKey key, bool set_params,
12801256
scan->rs_cbuf = InvalidBuffer;
12811257
}
12821258

1283-
if (scan->rs_base.rs_flags & SO_TYPE_BITMAPSCAN)
1284-
{
1285-
BitmapHeapScanDesc bscan = (BitmapHeapScanDesc) scan;
1286-
1287-
/*
1288-
* Reset empty_tuples_pending, a field only used by bitmap heap scan,
1289-
* to avoid incorrectly emitting NULL-filled tuples from a previous
1290-
* scan on rescan.
1291-
*/
1292-
bscan->rs_empty_tuples_pending = 0;
1293-
1294-
if (BufferIsValid(bscan->rs_vmbuffer))
1295-
{
1296-
ReleaseBuffer(bscan->rs_vmbuffer);
1297-
bscan->rs_vmbuffer = InvalidBuffer;
1298-
}
1299-
}
1259+
/*
1260+
* SO_TYPE_BITMAPSCAN would be cleaned up here, but it does not hold any
1261+
* additional data vs a normal HeapScan
1262+
*/
13001263

13011264
/*
13021265
* The read stream is reset on rescan. This must be done before
@@ -1325,15 +1288,6 @@ heap_endscan(TableScanDesc sscan)
13251288
if (BufferIsValid(scan->rs_cbuf))
13261289
ReleaseBuffer(scan->rs_cbuf);
13271290

1328-
if (scan->rs_base.rs_flags & SO_TYPE_BITMAPSCAN)
1329-
{
1330-
BitmapHeapScanDesc bscan = (BitmapHeapScanDesc) sscan;
1331-
1332-
bscan->rs_empty_tuples_pending = 0;
1333-
if (BufferIsValid(bscan->rs_vmbuffer))
1334-
ReleaseBuffer(bscan->rs_vmbuffer);
1335-
}
1336-
13371291
/*
13381292
* Must free the read stream before freeing the BufferAccessStrategy.
13391293
*/

src/backend/access/heap/heapam_handler.c

+2-42
Original file line numberDiff line numberDiff line change
@@ -2137,32 +2137,6 @@ heapam_scan_bitmap_next_tuple(TableScanDesc scan,
21372137
*/
21382138
while (hscan->rs_cindex >= hscan->rs_ntuples)
21392139
{
2140-
/*
2141-
* Emit empty tuples before advancing to the next block
2142-
*/
2143-
if (bscan->rs_empty_tuples_pending > 0)
2144-
{
2145-
/*
2146-
* If we don't have to fetch the tuple, just return nulls.
2147-
*/
2148-
ExecStoreAllNullTuple(slot);
2149-
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;
2163-
return true;
2164-
}
2165-
21662140
/*
21672141
* Returns false if the bitmap is exhausted and there are no further
21682142
* blocks we need to scan.
@@ -2516,24 +2490,10 @@ BitmapHeapScanNextBlock(TableScanDesc scan,
25162490

25172491
if (BufferIsInvalid(hscan->rs_cbuf))
25182492
{
2519-
if (BufferIsValid(bscan->rs_vmbuffer))
2520-
{
2521-
ReleaseBuffer(bscan->rs_vmbuffer);
2522-
bscan->rs_vmbuffer = InvalidBuffer;
2523-
}
2524-
25252493
/*
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.
2494+
* The bitmap is exhausted.
25342495
*/
2535-
*recheck = false;
2536-
return bscan->rs_empty_tuples_pending > 0;
2496+
return false;
25372497
}
25382498

25392499
Assert(per_buffer_data);

src/backend/executor/nodeBitmapHeapscan.c

+1-14
Original file line numberDiff line numberDiff line change
@@ -105,24 +105,11 @@ BitmapTableScanSetup(BitmapHeapScanState *node)
105105
*/
106106
if (!node->ss.ss_currentScanDesc)
107107
{
108-
bool need_tuples = false;
109-
110-
/*
111-
* We can potentially skip fetching heap pages if we do not need any
112-
* columns of the table, either for checking non-indexable quals or
113-
* for returning data. This test is a bit simplistic, as it checks
114-
* the stronger condition that there's no qual or return tlist at all.
115-
* But in most cases it's probably not worth working harder than that.
116-
*/
117-
need_tuples = (node->ss.ps.plan->qual != NIL ||
118-
node->ss.ps.plan->targetlist != NIL);
119-
120108
node->ss.ss_currentScanDesc =
121109
table_beginscan_bm(node->ss.ss_currentRelation,
122110
node->ss.ps.state->es_snapshot,
123111
0,
124-
NULL,
125-
need_tuples);
112+
NULL);
126113
}
127114

128115
node->ss.ss_currentScanDesc->st.rs_tbmiterator = tbmiterator;

src/include/access/heapam.h

+1-11
Original file line numberDiff line numberDiff line change
@@ -103,17 +103,7 @@ typedef struct BitmapHeapScanDescData
103103
{
104104
HeapScanDescData rs_heap_base;
105105

106-
/*
107-
* These fields are only used for bitmap scans for the "skip fetch"
108-
* optimization. Bitmap scans needing no fields from the heap may skip
109-
* fetching an all visible block, instead using the number of tuples per
110-
* block reported by the bitmap to determine how many NULL-filled tuples
111-
* to return. They are common to parallel and serial BitmapHeapScans
112-
*/
113-
114-
/* page of VM containing info for current block */
115-
Buffer rs_vmbuffer;
116-
int rs_empty_tuples_pending;
106+
/* Holds no data */
117107
} BitmapHeapScanDescData;
118108
typedef struct BitmapHeapScanDescData *BitmapHeapScanDesc;
119109

src/include/access/tableam.h

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

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

7467
/*
@@ -920,13 +913,10 @@ table_beginscan_strat(Relation rel, Snapshot snapshot,
920913
*/
921914
static inline TableScanDesc
922915
table_beginscan_bm(Relation rel, Snapshot snapshot,
923-
int nkeys, struct ScanKeyData *key, bool need_tuple)
916+
int nkeys, struct ScanKeyData *key)
924917
{
925918
uint32 flags = SO_TYPE_BITMAPSCAN | SO_ALLOW_PAGEMODE;
926919

927-
if (need_tuple)
928-
flags |= SO_NEED_TUPLES;
929-
930920
return rel->rd_tableam->scan_begin(rel, snapshot, nkeys, key,
931921
NULL, flags);
932922
}

0 commit comments

Comments
 (0)