From e1a050a893b6f4ee2edf91367ea76b6c2aa16c4a Mon Sep 17 00:00:00 2001
From: Andrey Borodin <amborodin@acm.org>
Date: Wed, 23 Oct 2024 20:46:50 +0300
Subject: [PATCH v4 2/4] Add TAP tests that verifies B-tree vacuum

---
 src/backend/access/nbtree/nbtree.c            |  5 ++
 src/test/modules/test_misc/meson.build        |  1 +
 .../modules/test_misc/t/007_vacuum_btree.pl   | 81 +++++++++++++++++++
 3 files changed, 87 insertions(+)
 create mode 100644 src/test/modules/test_misc/t/007_vacuum_btree.pl

diff --git a/src/backend/access/nbtree/nbtree.c b/src/backend/access/nbtree/nbtree.c
index 2dc89cb..bd88b33 100644
--- a/src/backend/access/nbtree/nbtree.c
+++ b/src/backend/access/nbtree/nbtree.c
@@ -34,6 +34,7 @@
 #include "storage/smgr.h"
 #include "utils/fmgrprotos.h"
 #include "utils/index_selfuncs.h"
+#include "utils/injection_point.h"
 #include "utils/memutils.h"
 
 
@@ -1066,6 +1067,10 @@ btvacuumscan(IndexVacuumInfo *info, IndexBulkDeleteResult *stats,
 		if (scanblkno >= num_pages)
 			break;
 
+		/* In 007_vacuum_btree test we need to coordinate two points here */
+		INJECTION_POINT("nbtree-vacuum-1");
+		INJECTION_POINT("nbtree-vacuum-2");
+
 		p.current_blocknum = scanblkno;
 		p.last_exclusive = num_pages;
 		/*
diff --git a/src/test/modules/test_misc/meson.build b/src/test/modules/test_misc/meson.build
index 283ffa7..a456a5e 100644
--- a/src/test/modules/test_misc/meson.build
+++ b/src/test/modules/test_misc/meson.build
@@ -15,6 +15,7 @@ tests += {
       't/004_io_direct.pl',
       't/005_timeouts.pl',
       't/006_signal_autovacuum.pl',
+      't/007_vacuum_btree.pl',
     ],
   },
 }
diff --git a/src/test/modules/test_misc/t/007_vacuum_btree.pl b/src/test/modules/test_misc/t/007_vacuum_btree.pl
new file mode 100644
index 0000000..e8df4c0
--- /dev/null
+++ b/src/test/modules/test_misc/t/007_vacuum_btree.pl
@@ -0,0 +1,81 @@
+# Copyright (c) 2024, PostgreSQL Global Development Group
+
+# This test verifies that B-tree vacuum can restart read stream.
+# To do so we need to insert some data during vacuum. So we wait in injection point
+# after first vacuum scan. During this wait we insert some data forcing page split.
+# this split will trigger relation extension and subsequent read_stream_reset().
+
+use strict;
+use warnings;
+use PostgreSQL::Test::Cluster;
+use Test::More;
+
+if ($ENV{enable_injection_points} ne 'yes')
+{
+	plan skip_all => 'Injection points not supported by this build';
+}
+
+# Initialize postgres
+my $psql_err = '';
+my $psql_out = '';
+my $node = PostgreSQL::Test::Cluster->new('node');
+$node->init;
+
+# This ensures autovacuum do not run
+$node->append_conf('postgresql.conf', 'autovacuum = off');
+$node->start;
+
+# Check if the extension injection_points is available, as it may be
+# possible that this script is run with installcheck, where the module
+# would not be installed by default.
+if (!$node->check_extension('injection_points'))
+{
+	plan skip_all => 'Extension injection_points not installed';
+}
+
+$node->safe_psql('postgres', 'CREATE EXTENSION injection_points;');
+
+# From this point, vacuum worker will wait at startup.
+$node->safe_psql('postgres',
+	"SELECT injection_points_attach('nbtree-vacuum-2', 'wait');");
+
+my $psql_session = $node->background_psql('postgres');
+
+$psql_session->query_until(
+	qr/starting_bg_psql/,
+		q(\echo starting_bg_psql
+		create table a as select random() r from generate_series(1,100) x;
+		create index on a(r);
+		delete from a;
+		vacuum a;
+	));
+
+#print $node->safe_psql('postgres','select * from pg_stat_activity');
+
+# Wait until an vacuum worker starts.
+$node->wait_for_event('client backend', 'nbtree-vacuum-2');
+
+$node->safe_psql('postgres',
+	"SELECT injection_points_attach('nbtree-vacuum-1', 'wait');");
+
+# Here's the key point of a test: during vacuum we add some page splits.
+# This will force vacuum into doing another scan thus reseting read stream.
+$node->safe_psql('postgres',
+	"insert into a select x from generate_series(1,3000) x;");
+
+$node->safe_psql('postgres',
+	"SELECT injection_points_detach('nbtree-vacuum-2');");
+$node->safe_psql('postgres',
+	"SELECT injection_points_wakeup('nbtree-vacuum-2');");
+
+# Observe that second scan is reached.
+$node->wait_for_event('client backend', 'nbtree-vacuum-1');
+
+$node->safe_psql('postgres',
+	"SELECT injection_points_detach('nbtree-vacuum-1');");
+$node->safe_psql('postgres',
+	"SELECT injection_points_wakeup('nbtree-vacuum-1');");
+
+ok($psql_session->quit);
+
+done_testing();
-- 
2.39.5 (Apple Git-154)

