Fix bitmap table scan crash on iterator release
authorMelanie Plageman <[email protected]>
Thu, 19 Dec 2024 16:55:03 +0000 (11:55 -0500)
committerMelanie Plageman <[email protected]>
Thu, 19 Dec 2024 16:55:03 +0000 (11:55 -0500)
1a0da347a7ac98db replaced Bitmap Table Scan's individual private and
shared iterators with a unified iterator. It neglected, however, to
check if the iterator had already been cleaned up before doing so on
rescan. Add this check both on rescan and end scan to be safe.

Reported-by: Richard Guo
Author: Richard Guo
Discussion: https://postgr.es/m/CAMbWs48nrhcLY1kcd-u9oD%2B6yiS631F_8Fx8ZGsO-BYDwH%2Bbyw%40mail.gmail.com

src/backend/executor/nodeBitmapHeapscan.c
src/backend/nodes/tidbitmap.c
src/test/regress/expected/join.out
src/test/regress/sql/join.sql

index a7cbaf1660e57f5a176e070f304530bda538dd68..928c94cab6a9ed9fb426165c73bc938bd28cb06d 100644 (file)
@@ -572,9 +572,11 @@ ExecReScanBitmapHeapScan(BitmapHeapScanState *node)
    if (scan)
    {
        /*
-        * End iteration on iterators saved in scan descriptor.
+        * End iteration on iterators saved in scan descriptor if they have
+        * not already been cleaned up.
         */
-       tbm_end_iterate(&scan->st.rs_tbmiterator);
+       if (!tbm_exhausted(&scan->st.rs_tbmiterator))
+           tbm_end_iterate(&scan->st.rs_tbmiterator);
 
        /* rescan to release any page pin */
        table_rescan(node->ss.ss_currentScanDesc, NULL);
@@ -654,9 +656,11 @@ ExecEndBitmapHeapScan(BitmapHeapScanState *node)
    if (scanDesc)
    {
        /*
-        * End iteration on iterators saved in scan descriptor.
+        * End iteration on iterators saved in scan descriptor if they have
+        * not already been cleaned up.
         */
-       tbm_end_iterate(&scanDesc->st.rs_tbmiterator);
+       if (!tbm_exhausted(&scanDesc->st.rs_tbmiterator))
+           tbm_end_iterate(&scanDesc->st.rs_tbmiterator);
 
        /*
         * close table scan
index 687d6f1763632820a136a0f151324bb7aea5b426..63316d5d6553af47d7b5dcf73464ea15015f9a50 100644 (file)
@@ -1594,7 +1594,7 @@ tbm_begin_iterate(TIDBitmap *tbm, dsa_area *dsa, dsa_pointer dsp)
 void
 tbm_end_iterate(TBMIterator *iterator)
 {
-   Assert(iterator);
+   Assert(iterator && !tbm_exhausted(iterator));
 
    if (iterator->shared)
        tbm_end_shared_iterate(iterator->i.shared_iterator);
index 55d44a9bceea41b2d7e56f3d93c71e600d56e880..0c9b312eafdf3c37520a340d5f3fbd2d0efe77ff 100644 (file)
@@ -8182,3 +8182,38 @@ SELECT t1.a FROM skip_fetch t1 LEFT JOIN skip_fetch t2 ON t2.a = 1 WHERE t2.a IS
 
 RESET enable_indexonlyscan;
 RESET enable_seqscan;
+-- Test BitmapHeapScan with a rescan releases resources correctly
+SET enable_seqscan = off;
+SET enable_indexscan = off;
+CREATE TEMP TABLE rescan_bhs (a INT);
+INSERT INTO rescan_bhs VALUES (1), (2);
+CREATE INDEX ON rescan_bhs (a);
+EXPLAIN (COSTS OFF)
+SELECT * FROM rescan_bhs t1 LEFT JOIN rescan_bhs t2 ON t1.a IN
+  (SELECT a FROM rescan_bhs t3 WHERE t2.a > 1);
+                        QUERY PLAN                         
+-----------------------------------------------------------
+ Nested Loop Left Join
+   Join Filter: (ANY (t1.a = (SubPlan 1).col1))
+   ->  Bitmap Heap Scan on rescan_bhs t1
+         ->  Bitmap Index Scan on rescan_bhs_a_idx
+   ->  Materialize
+         ->  Bitmap Heap Scan on rescan_bhs t2
+               ->  Bitmap Index Scan on rescan_bhs_a_idx
+   SubPlan 1
+     ->  Result
+           One-Time Filter: (t2.a > 1)
+           ->  Bitmap Heap Scan on rescan_bhs t3
+                 ->  Bitmap Index Scan on rescan_bhs_a_idx
+(12 rows)
+
+SELECT * FROM rescan_bhs t1 LEFT JOIN rescan_bhs t2 ON t1.a IN
+  (SELECT a FROM rescan_bhs t3 WHERE t2.a > 1);
+ a | a 
+---+---
+ 1 | 2
+ 2 | 2
+(2 rows)
+
+RESET enable_seqscan;
+RESET enable_indexscan;
index d6f49f281b7f4cc934b6715e2b9af8452da92036..8cfc1053cb78b1061ec28615dd723c1a8922f2a2 100644 (file)
@@ -3016,3 +3016,20 @@ SELECT t1.a FROM skip_fetch t1 LEFT JOIN skip_fetch t2 ON t2.a = 1 WHERE t2.a IS
 
 RESET enable_indexonlyscan;
 RESET enable_seqscan;
+
+-- Test BitmapHeapScan with a rescan releases resources correctly
+SET enable_seqscan = off;
+SET enable_indexscan = off;
+
+CREATE TEMP TABLE rescan_bhs (a INT);
+INSERT INTO rescan_bhs VALUES (1), (2);
+CREATE INDEX ON rescan_bhs (a);
+
+EXPLAIN (COSTS OFF)
+SELECT * FROM rescan_bhs t1 LEFT JOIN rescan_bhs t2 ON t1.a IN
+  (SELECT a FROM rescan_bhs t3 WHERE t2.a > 1);
+SELECT * FROM rescan_bhs t1 LEFT JOIN rescan_bhs t2 ON t1.a IN
+  (SELECT a FROM rescan_bhs t3 WHERE t2.a > 1);
+
+RESET enable_seqscan;
+RESET enable_indexscan;