From 3b189a59b2e59220ed192d8dde4080659f76fec9 Mon Sep 17 00:00:00 2001
From: Peter Geoghegan <pg@bowt.ie>
Date: Mon, 2 Dec 2024 15:50:04 -0500
Subject: [PATCH v1] isolationtester showing broken index-only scans with GiST

---
 .../expected/index-only-gistscan.out          | 28 +++++++
 src/test/isolation/isolation_schedule         |  1 +
 .../isolation/specs/index-only-gistscan.spec  | 79 +++++++++++++++++++
 3 files changed, 108 insertions(+)
 create mode 100644 src/test/isolation/expected/index-only-gistscan.out
 create mode 100644 src/test/isolation/specs/index-only-gistscan.spec

diff --git a/src/test/isolation/expected/index-only-gistscan.out b/src/test/isolation/expected/index-only-gistscan.out
new file mode 100644
index 000000000..5c97d0f27
--- /dev/null
+++ b/src/test/isolation/expected/index-only-gistscan.out
@@ -0,0 +1,28 @@
+Parsed test spec with 2 sessions
+
+starting permutation: s2_vacuum s2_mod 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_begin: BEGIN;
+step s1_prepare: 
+    DECLARE foo NO SCROLL CURSOR FOR SELECT a FROM ios_bitmap WHERE a > 0;
+
+step s1_fetch_1: 
+    FETCH FROM foo;
+
+a
+-
+1
+(1 row)
+
+step s2_vacuum: VACUUM (TRUNCATE false) ios_bitmap;
+step s1_fetch_all: 
+    FETCH ALL FROM foo;
+
+a
+-
+(0 rows)
+
+step s1_commit: COMMIT;
diff --git a/src/test/isolation/isolation_schedule b/src/test/isolation/isolation_schedule
index 143109aa4..e3c669a29 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-gistscan.spec b/src/test/isolation/specs/index-only-gistscan.spec
new file mode 100644
index 000000000..f42bcd80f
--- /dev/null
+++ b/src/test/isolation/specs/index-only-gistscan.spec
@@ -0,0 +1,79 @@
+# index-only-scan test showing wrong results with GiST
+#
+setup
+{
+    -- by using a low fillfactor and a wide tuple we can get multiple blocks
+    -- with just few rows
+    create extension btree_gist;
+    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_gist_a ON ios_bitmap USING GIST (a);
+}
+
+teardown
+{
+    DROP TABLE ios_bitmap;
+}
+
+
+session s1
+
+setup {
+    SET enable_seqscan = false;
+    SET enable_bitmapscan = false;
+}
+
+step s1_begin { BEGIN; }
+step s1_commit { COMMIT; }
+
+step s1_prepare {
+    DECLARE foo NO SCROLL CURSOR FOR SELECT a FROM ios_bitmap WHERE a > 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
+  # 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 will mark pages as all-visible that the scan in
+  # the next step then considers all-visible, despite all rows from those
+  # pages having been removed.
+  s2_vacuum
+  # if this returns any rows, we're busted
+  s1_fetch_all
+
+  s1_commit
-- 
2.45.2

