summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndres Freund2025-04-02 18:58:39 +0000
committerAndres Freund2025-04-02 18:58:39 +0000
commit24da5b239a4b50798fb2cbe46597cc932a5210f2 (patch)
treef14f8293d84baaffa284fabfc16afafd2290a2a7
parent459e7bf8e2f8ab894dc613fa8555b74c4eef6969 (diff)
Add test for HeapBitmapScan's broken skip_fetch optimization
In the previous commit HeapBitmapScan's skip_fetch optimization was removed, due to being broken in not easily fixable ways. Add a test that verifies we don't re-introduce this bug if somebody tries to re-add the feature. Only add the test to master for now, it's possible it's not entirely stable. That seems sufficient, as we're not going to re-introduce the feature on the backbranches. I did verify that the test passes on all branches. If the test turns out to be unproblematic, we can backpatch it later, should we feel a need to do so. Discussion: https://postgr.es/m/CAEze2Wg3gXXZTr6_rwC+s4-o2ZVFB5F985uUSgJTsECx6AmGcQ@mail.gmail.com
-rw-r--r--src/test/isolation/expected/index-only-bitmapscan.out48
-rw-r--r--src/test/isolation/isolation_schedule1
-rw-r--r--src/test/isolation/specs/index-only-bitmapscan.spec108
3 files changed, 157 insertions, 0 deletions
diff --git a/src/test/isolation/expected/index-only-bitmapscan.out b/src/test/isolation/expected/index-only-bitmapscan.out
new file mode 100644
index 00000000000..9f27df20153
--- /dev/null
+++ b/src/test/isolation/expected/index-only-bitmapscan.out
@@ -0,0 +1,48 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s2_vacuum s2_mod s1_explain s1_begin s1_prepare s1_fetch_1 s2_vacuum s1_fetch_all s1_commit
+step s2_vacuum:
+ VACUUM (TRUNCATE false) ios_bitmap;
+
+step s2_mod:
+ DELETE FROM ios_bitmap WHERE a > 1;
+
+step s1_explain:
+ EXPlAIN (COSTS OFF) DECLARE foo NO SCROLL CURSOR FOR SELECT row_number() OVER () FROM ios_bitmap WHERE a > 0 or b > 0;
+
+QUERY PLAN
+---------------------------------------------------
+WindowAgg
+ Window: w1 AS (ROWS UNBOUNDED PRECEDING)
+ -> Bitmap Heap Scan on ios_bitmap
+ Recheck Cond: ((a > 0) OR (b > 0))
+ -> BitmapOr
+ -> Bitmap Index Scan on ios_bitmap_a
+ Index Cond: (a > 0)
+ -> Bitmap Index Scan on ios_bitmap_b
+ Index Cond: (b > 0)
+(9 rows)
+
+step s1_begin: BEGIN;
+step s1_prepare:
+ DECLARE foo NO SCROLL CURSOR FOR SELECT row_number() OVER () FROM ios_bitmap WHERE a > 0 or b > 0;
+
+step s1_fetch_1:
+ FETCH FROM foo;
+
+row_number
+----------
+ 1
+(1 row)
+
+step s2_vacuum:
+ VACUUM (TRUNCATE false) ios_bitmap;
+
+step s1_fetch_all:
+ FETCH ALL FROM foo;
+
+row_number
+----------
+(0 rows)
+
+step s1_commit: COMMIT;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 143109aa4da..e3c669a29c7 100644
--- a/src/test/isolation/isolation_schedule
+++ b/src/test/isolation/isolation_schedule
@@ -17,6 +17,7 @@ test: partial-index
test: two-ids
test: multiple-row-versions
test: index-only-scan
+test: index-only-bitmapscan
test: predicate-lock-hot-tuple
test: update-conflict-out
test: deadlock-simple
diff --git a/src/test/isolation/specs/index-only-bitmapscan.spec b/src/test/isolation/specs/index-only-bitmapscan.spec
new file mode 100644
index 00000000000..4cd8b3ccdef
--- /dev/null
+++ b/src/test/isolation/specs/index-only-bitmapscan.spec
@@ -0,0 +1,108 @@
+# In the past we supported index-only bitmap heapscans. However the
+# implementation was unsound, see
+# https://postgr.es/m/873c33c5-ef9e-41f6-80b2-2f5e11869f1c%40garret.ru
+#
+# This test reliably triggered the problem before we removed the
+# optimization. We keep the test around to make it less likely for a similar
+# problem to be re-introduced.
+
+setup
+{
+ -- by using a low fillfactor and a wide tuple we can get multiple blocks
+ -- with just few rows
+ CREATE TABLE ios_bitmap (a int NOT NULL, b int not null, pad char(1024) default '')
+ WITH (AUTOVACUUM_ENABLED = false, FILLFACTOR = 10);
+
+ INSERT INTO ios_bitmap SELECT g.i, g.i FROM generate_series(1, 10) g(i);
+
+ CREATE INDEX ios_bitmap_a ON ios_bitmap(a);
+ CREATE INDEX ios_bitmap_b ON ios_bitmap(b);
+}
+
+teardown
+{
+ DROP TABLE ios_bitmap;
+}
+
+
+session s1
+
+setup
+{
+ SET enable_seqscan = false;
+}
+
+step s1_begin { BEGIN; }
+step s1_commit { COMMIT; }
+
+
+# The test query uses an or between two indexes to ensure make it more likely
+# to use a bitmap index scan
+#
+# The row_number() hack is a way to have something returned (isolationtester
+# doesn't display empty rows) while still allowing for the index-only scan
+# optimization in bitmap heap scans, which requires an empty targetlist.
+step s1_prepare
+{
+ DECLARE foo NO SCROLL CURSOR FOR SELECT row_number() OVER () FROM ios_bitmap WHERE a > 0 or b > 0;
+}
+
+step s1_explain
+{
+ EXPlAIN (COSTS OFF) DECLARE foo NO SCROLL CURSOR FOR SELECT row_number() OVER () FROM ios_bitmap WHERE a > 0 or b > 0;
+}
+
+step s1_fetch_1
+{
+ FETCH FROM foo;
+}
+
+step s1_fetch_all
+{
+ FETCH ALL FROM foo;
+}
+
+
+session s2
+
+# Don't delete row 1 so we have a row for the cursor to "rest" on.
+step s2_mod
+{
+ DELETE FROM ios_bitmap WHERE a > 1;
+}
+
+# Disable truncation, as otherwise we'll just wait for a timeout while trying
+# to acquire the lock
+step s2_vacuum
+{
+ VACUUM (TRUNCATE false) ios_bitmap;
+}
+
+permutation
+ # Vacuum first, to ensure VM exists, otherwise the bitmapscan will consider
+ # VM to be size 0, due to caching. Can't do that in setup because
+ s2_vacuum
+
+ # Delete nearly all rows, to make issue visible
+ s2_mod
+
+ # Verify that the appropriate plan is chosen
+ s1_explain
+
+ # Create a cursor
+ s1_begin
+ s1_prepare
+
+ # Fetch one row from the cursor, that ensures the index scan portion is done
+ # before the vacuum in the next step
+ s1_fetch_1
+
+ # With the bug this vacuum would have marked pages as all-visible that the
+ # scan in the next step then would have considered all-visible, despite all
+ # rows from those pages having been removed.
+ s2_vacuum
+
+ # If this returns any rows, the bug is present
+ s1_fetch_all
+
+ s1_commit