From 4cc9f3d4b5aa8c3544599c47ab3ec9a62a96381d Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
Date: Mon, 6 Apr 2026 19:40:56 +0200
Subject: [PATCH v5 2/2] Test VACUUM FULL, CLUSTER, and REPACK with locked temp
 tables

This test creates a background session with a temp table and marks
its index as clustered (making it visible to both the pg_class scan
used by VACUUM FULL and REPACK, and the pg_index scan used by CLUSTER),
then holds ACCESS SHARE LOCK in an open transaction.  Each command
runs with lock_timeout = '1ms'.  Since lock_timeout only fires when a
backend actually blocks waiting for a lock, 1ms is sufficient.
---
 src/test/modules/test_misc/meson.build        |  1 +
 .../t/012_vacuum_cluster_temp_tables.pl       | 65 +++++++++++++++++++
 2 files changed, 66 insertions(+)
 create mode 100644 src/test/modules/test_misc/t/012_vacuum_cluster_temp_tables.pl

diff --git a/src/test/modules/test_misc/meson.build b/src/test/modules/test_misc/meson.build
index 1b25d98f7f3..8995bc837f2 100644
--- a/src/test/modules/test_misc/meson.build
+++ b/src/test/modules/test_misc/meson.build
@@ -20,6 +20,7 @@ tests += {
       't/009_log_temp_files.pl',
       't/010_index_concurrently_upsert.pl',
       't/011_lock_stats.pl',
+      't/012_vacuum_cluster_temp_tables.pl',
     ],
     # The injection points are cluster-wide, so disable installcheck
     'runningcheck': false,
diff --git a/src/test/modules/test_misc/t/012_vacuum_cluster_temp_tables.pl b/src/test/modules/test_misc/t/012_vacuum_cluster_temp_tables.pl
new file mode 100644
index 00000000000..a612b4d6361
--- /dev/null
+++ b/src/test/modules/test_misc/t/012_vacuum_cluster_temp_tables.pl
@@ -0,0 +1,65 @@
+# Copyright (c) 2026, PostgreSQL Global Development Group
+#
+# Verify that no-argument VACUUM FULL, CLUSTER, and REPACK skip temporary
+# tables belonging to other sessions.
+#
+# A background session creates a temp table and marks its index as clustered —
+# making it visible to both the pg_class scan (VACUUM FULL, REPACK) and the
+# pg_index scan (CLUSTER) — then holds ACCESS SHARE LOCK in an open transaction.
+# Each command runs with lock_timeout = '1ms'. Since lock_timeout only
+# fires when a backend actually blocks waiting for a lock, 1ms is sufficient.
+
+use strict;
+use warnings;
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+my $node = PostgreSQL::Test::Cluster->new('vacuum_cluster_temp');
+$node->init;
+$node->start;
+
+# Session 1: build the temp table and hold a conflicting lock.
+my $psql1 = $node->background_psql('postgres');
+
+$psql1->query_safe(
+	q{CREATE TEMP TABLE temp_repack_test (val int);
+	  INSERT INTO temp_repack_test VALUES (1);
+	  CREATE INDEX temp_repack_idx ON temp_repack_test (val);
+	  CLUSTER temp_repack_test USING temp_repack_idx;});
+
+$psql1->query_safe(q{BEGIN});
+$psql1->query_safe(q{LOCK TABLE temp_repack_test IN ACCESS SHARE MODE});
+
+my ($stdout, $stderr, $ret);
+
+# VACUUM FULL — pg_class scan path.
+$ret = $node->psql(
+	'postgres',
+	"SET lock_timeout = '1ms'; VACUUM FULL;",
+	stdout => \$stdout,
+	stderr => \$stderr);
+is($ret, 0,
+	'VACUUM FULL completes without blocking on another session temp table');
+
+# CLUSTER — pg_index scan path (indisclustered entries).
+$ret = $node->psql(
+	'postgres',
+	"SET lock_timeout = '1ms'; CLUSTER;",
+	stdout => \$stdout,
+	stderr => \$stderr);
+is($ret, 0,
+	'CLUSTER completes without blocking on another session temp table');
+
+# REPACK — pg_class scan path.
+$ret = $node->psql(
+	'postgres',
+	"SET lock_timeout = '1ms'; REPACK;",
+	stdout => \$stdout,
+	stderr => \$stderr);
+is($ret, 0,
+	'REPACK completes without blocking on another session temp table');
+
+$psql1->quit;
+
+done_testing();
-- 
2.43.0

