From 7e1142e46f1934eae975ca672f4823512ad1d146 Mon Sep 17 00:00:00 2001
From: Zhong ShiHao <zhong950419@gmail.com>
Date: Fri, 4 Sep 2026 22:11:27 -0400
Subject: [PATCH 1/2] Test GiST page recycling and deleting all entries

gist.sql vacuumed only once after deleting rows, so the emptied leaf pages
were never recycled or reused, and no test deleted every entry.
---
 src/test/regress/expected/gist.out | 57 ++++++++++++++++++++++++++++++
 src/test/regress/sql/gist.sql      | 35 ++++++++++++++++++
 2 files changed, 92 insertions(+)

diff --git a/src/test/regress/expected/gist.out b/src/test/regress/expected/gist.out
index ac79f94aa80..afe88ad9c2b 100644
--- a/src/test/regress/expected/gist.out
+++ b/src/test/regress/expected/gist.out
@@ -30,6 +30,63 @@ delete from gist_point_tbl where id % 2 = 1;
 -- And also delete some concentration of values.
 delete from gist_point_tbl where id > 5000;
 vacuum analyze gist_point_tbl;
+-- The empty leaf pages deleted above can only be recycled once the deleting
+-- transaction is behind the xid horizon, so assign a new xid before vacuuming
+-- again.  Reinserting into the emptied range then reuses the recycled pages.
+select pg_current_xact_id() is not null;
+ ?column? 
+----------
+ t
+(1 row)
+
+vacuum gist_point_tbl;
+insert into gist_point_tbl (id, p)
+select g, point(g*10, g*10) from generate_series(5001, 7000) g;
+-- The index must agree with a sequential scan after all of that.
+set enable_seqscan = off;
+set enable_bitmapscan = off;
+select count(*) from gist_point_tbl where p <@ box(point(0,0), point(100000,100000));
+ count 
+-------
+  4500
+(1 row)
+
+reset enable_seqscan;
+reset enable_bitmapscan;
+set enable_indexscan = off;
+set enable_bitmapscan = off;
+set enable_indexonlyscan = off;
+select count(*) from gist_point_tbl where p <@ box(point(0,0), point(100000,100000));
+ count 
+-------
+  4500
+(1 row)
+
+reset enable_indexscan;
+reset enable_bitmapscan;
+reset enable_indexonlyscan;
+-- Deleting everything must leave the last downlink on each internal page in
+-- place, and the index must still be usable afterwards.
+delete from gist_point_tbl;
+vacuum gist_point_tbl;
+set enable_seqscan = off;
+set enable_bitmapscan = off;
+select count(*) from gist_point_tbl where p <@ box(point(0,0), point(100000,100000));
+ count 
+-------
+     0
+(1 row)
+
+insert into gist_point_tbl (id, p)
+select g, point(g*10, g*10) from generate_series(1, 2000) g;
+select count(*) from gist_point_tbl where p <@ box(point(0,0), point(100000,100000));
+ count 
+-------
+  2000
+(1 row)
+
+reset enable_seqscan;
+reset enable_bitmapscan;
 -- rebuild the index with a different fillfactor
 alter index gist_pointidx SET (fillfactor = 40);
 reindex index gist_pointidx;
diff --git a/src/test/regress/sql/gist.sql b/src/test/regress/sql/gist.sql
index 57dcc082450..363a3e3c314 100644
--- a/src/test/regress/sql/gist.sql
+++ b/src/test/regress/sql/gist.sql
@@ -33,6 +33,41 @@ delete from gist_point_tbl where id > 5000;
 
 vacuum analyze gist_point_tbl;
 
+-- The empty leaf pages deleted above can only be recycled once the deleting
+-- transaction is behind the xid horizon, so assign a new xid before vacuuming
+-- again.  Reinserting into the emptied range then reuses the recycled pages.
+select pg_current_xact_id() is not null;
+vacuum gist_point_tbl;
+insert into gist_point_tbl (id, p)
+select g, point(g*10, g*10) from generate_series(5001, 7000) g;
+
+-- The index must agree with a sequential scan after all of that.
+set enable_seqscan = off;
+set enable_bitmapscan = off;
+select count(*) from gist_point_tbl where p <@ box(point(0,0), point(100000,100000));
+reset enable_seqscan;
+reset enable_bitmapscan;
+set enable_indexscan = off;
+set enable_bitmapscan = off;
+set enable_indexonlyscan = off;
+select count(*) from gist_point_tbl where p <@ box(point(0,0), point(100000,100000));
+reset enable_indexscan;
+reset enable_bitmapscan;
+reset enable_indexonlyscan;
+
+-- Deleting everything must leave the last downlink on each internal page in
+-- place, and the index must still be usable afterwards.
+delete from gist_point_tbl;
+vacuum gist_point_tbl;
+set enable_seqscan = off;
+set enable_bitmapscan = off;
+select count(*) from gist_point_tbl where p <@ box(point(0,0), point(100000,100000));
+insert into gist_point_tbl (id, p)
+select g, point(g*10, g*10) from generate_series(1, 2000) g;
+select count(*) from gist_point_tbl where p <@ box(point(0,0), point(100000,100000));
+reset enable_seqscan;
+reset enable_bitmapscan;
+
 -- rebuild the index with a different fillfactor
 alter index gist_pointidx SET (fillfactor = 40);
 reindex index gist_pointidx;
-- 
2.37.1 (Apple Git-137.1)

