Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 459e7bf

Browse files
committed
Remove HeapBitmapScan's skip_fetch optimization
The optimization does not take the removal of TIDs by a concurrent vacuum into account. The concurrent vacuum can remove dead TIDs and make pages ALL_VISIBLE while those dead TIDs are referenced in the bitmap. This can lead to a skip_fetch scan returning too many tuples. It likely would be possible to implement this optimization safely, but we don't have the necessary infrastructure in place. Nor is it clear that it's worth building that infrastructure, given how limited the skip_fetch optimization is. In the backbranches we just disable the optimization by always passing need_tuples=true to table_beginscan_bm(). We can't perform API/ABI changes in the backbranches and we want to make the change as minimal as possible. Author: Matthias van de Meent <[email protected]> Reported-By: Konstantin Knizhnik <[email protected]> Discussion: https://postgr.es/m/CAEze2Wg3gXXZTr6_rwC+s4-o2ZVFB5F985uUSgJTsECx6AmGcQ@mail.gmail.com Backpatch-through: 13
1 parent 0dca5d6 commit 459e7bf

File tree

5 files changed

+13
-133
lines changed

5 files changed

+13
-133
lines changed

src/backend/access/heap/heapam.c

Lines changed: 8 additions & 53 deletions
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

@@ -1124,8 +1099,10 @@ heap_beginscan(Relation relation, Snapshot snapshot,
11241099
{
11251100
BitmapHeapScanDesc bscan = palloc(sizeof(BitmapHeapScanDescData));
11261101

1127-
bscan->rs_vmbuffer = InvalidBuffer;
1128-
bscan->rs_empty_tuples_pending = 0;
1102+
/*
1103+
* Bitmap Heap scans do not have any fields that a normal Heap Scan
1104+
* does not have, so no special initializations required here.
1105+
*/
11291106
scan = (HeapScanDesc) bscan;
11301107
}
11311108
else
@@ -1280,23 +1257,10 @@ heap_rescan(TableScanDesc sscan, ScanKey key, bool set_params,
12801257
scan->rs_cbuf = InvalidBuffer;
12811258
}
12821259

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-
}
1260+
/*
1261+
* SO_TYPE_BITMAPSCAN would be cleaned up here, but it does not hold any
1262+
* additional data vs a normal HeapScan
1263+
*/
13001264

13011265
/*
13021266
* The read stream is reset on rescan. This must be done before
@@ -1325,15 +1289,6 @@ heap_endscan(TableScanDesc sscan)
13251289
if (BufferIsValid(scan->rs_cbuf))
13261290
ReleaseBuffer(scan->rs_cbuf);
13271291

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-
13371292
/*
13381293
* Must free the read stream before freeing the BufferAccessStrategy.
13391294
*/

src/backend/access/heap/heapam_handler.c

Lines changed: 2 additions & 44 deletions
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,8 @@ 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-
2525-
/*
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.
2534-
*/
2535-
*recheck = false;
2536-
return bscan->rs_empty_tuples_pending > 0;
2493+
/* the bitmap is exhausted */
2494+
return false;
25372495
}
25382496

25392497
Assert(per_buffer_data);

src/backend/executor/nodeBitmapHeapscan.c

Lines changed: 1 addition & 14 deletions
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

Lines changed: 1 addition & 11 deletions
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

Lines changed: 1 addition & 11 deletions
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)