Hi

On 13/04/2026 14:40, Soumya S Murali wrote:
> Please let me know if there are additional scenarios I should
> validate. Looking forward to more feedback.

Thanks for testing it. You can take a look at
012_temp_obj_multisession.pl and check if we missed any path.

Due to changes introduced in b2a17ba7a5d the patch was no longer
applying. See rebased v18 attached.

Best, Jim

From 88a3cd38d035075ef09c923ab3c7f358389e1022 Mon Sep 17 00:00:00 2001
From: Jim Jones <[email protected]>
Date: Mon, 13 Apr 2026 15:28:35 +0200
Subject: [PATCH v18 2/2] Test cross-session access on temporary tables

---
 src/test/modules/test_misc/meson.build        |   1 +
 .../test_misc/t/012_temp_obj_multisession.pl  | 109 ++++++++++++++++++
 2 files changed, 110 insertions(+)
 create mode 100644 src/test/modules/test_misc/t/012_temp_obj_multisession.pl

diff --git a/src/test/modules/test_misc/meson.build b/src/test/modules/test_misc/meson.build
index 1b25d98f7f3..a54599cc301 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_temp_obj_multisession.pl',
     ],
     # The injection points are cluster-wide, so disable installcheck
     'runningcheck': false,
diff --git a/src/test/modules/test_misc/t/012_temp_obj_multisession.pl b/src/test/modules/test_misc/t/012_temp_obj_multisession.pl
new file mode 100644
index 00000000000..fa8037e4d1e
--- /dev/null
+++ b/src/test/modules/test_misc/t/012_temp_obj_multisession.pl
@@ -0,0 +1,109 @@
+# Copyright (c) 2026, PostgreSQL Global Development Group
+
+use strict;
+use warnings;
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use PostgreSQL::Test::BackgroundPsql;
+use Test::More;
+
+# Set up a fresh node
+my $node = PostgreSQL::Test::Cluster->new('temp_lock');
+$node->init;
+$node->start;
+
+# Create a long-lived session
+my $psql1 = $node->background_psql('postgres');
+
+$psql1->query_safe(q(CREATE TEMP TABLE foo AS SELECT 42 AS val;));
+
+$psql1->query_safe(q(CREATE INDEX ON foo(val);));
+
+my $tempschema = $node->safe_psql(
+	'postgres',
+	q{
+      SELECT n.nspname
+      FROM pg_class c
+      JOIN pg_namespace n ON n.oid = c.relnamespace
+      WHERE relname = 'foo' AND relpersistence = 't';
+    }
+);
+chomp $tempschema;
+ok($tempschema =~ /^pg_temp_\d+$/, "got temp schema: $tempschema");
+
+
+# SELECT TEMPORARY TABLE from other session
+my ($stdout, $stderr);
+$node->psql(
+	'postgres',
+	"SELECT val FROM $tempschema.foo;",
+	stdout => \$stdout,
+	stderr => \$stderr);
+like(
+	$stderr,
+	qr/cannot access temporary relations of other sessions/,
+	'SELECT on other session temp table is not allowed');
+
+# UPDATE TEMPORARY TABLE from other session
+$node->psql(
+	'postgres',
+	"UPDATE $tempschema.foo SET val = NULL;",
+	stderr => \$stderr);
+like(
+	$stderr,
+	qr/cannot access temporary relations of other sessions/,
+	'UPDATE on other session temp table is not allowed');
+
+# DELETE records from TEMPORARY TABLE from other session
+$node->psql('postgres', "DELETE FROM $tempschema.foo;", stderr => \$stderr);
+like(
+	$stderr,
+	qr/cannot access temporary relations of other sessions/,
+	'DELETE on other session temp table is not allowed');
+
+# TRUNCATE TEMPORARY TABLE from other session
+$node->psql('postgres', "TRUNCATE TABLE $tempschema.foo;",
+	stderr => \$stderr);
+like(
+	$stderr,
+	qr/cannot truncate temporary tables of other sessions/,
+	'TRUNCATE on other session temp table is not allowed');
+
+# INSERT INTO TEMPORARY TABLE from other session
+$node->psql(
+	'postgres',
+	"INSERT INTO $tempschema.foo VALUES (73);",
+	stderr => \$stderr);
+like(
+	$stderr,
+	qr/cannot access temporary relations of other sessions/,
+	'INSERT INTO on other session temp table is not allowed');
+
+# COPY TEMPORARY TABLE from other session
+$node->psql('postgres', "COPY $tempschema.foo TO STDOUT;",
+	stderr => \$stderr);
+like(
+	$stderr,
+	qr/cannot access temporary relations of other sessions/,
+	'COPY on other session temp table is blocked');
+
+# Index scans can use a different code path from the one sequential scans are
+# following. Make sure that we cannot access other sessions' temp tables during
+# index scan either.
+$node->psql(
+	'postgres',
+	"SET enable_seqscan = off; SELECT val FROM $tempschema.foo WHERE val = 42;",
+	stderr => \$stderr);
+like(
+	$stderr,
+	qr/cannot access temporary relations of other sessions/,
+	'index scan on other session temp table is not allowed (exercises ReadBufferExtended path)'
+);
+
+# DROP TEMPORARY TABLE from other session
+$node->safe_psql('postgres', "DROP TABLE $tempschema.foo;");
+
+# Clean up
+$psql1->quit;
+
+done_testing();
-- 
2.43.0

From 635e924e26a772ddccaecc52cb994650f261c290 Mon Sep 17 00:00:00 2001
From: Jim Jones <[email protected]>
Date: Mon, 13 Apr 2026 15:17:20 +0200
Subject: [PATCH v18 1/2] Prevent access to other sessions' temp tables

---
 src/backend/storage/aio/read_stream.c | 10 ++++++++++
 src/backend/storage/buffer/bufmgr.c   | 16 ++++++++++++++--
 2 files changed, 24 insertions(+), 2 deletions(-)

diff --git a/src/backend/storage/aio/read_stream.c b/src/backend/storage/aio/read_stream.c
index 2374b4cd507..6ba6774af95 100644
--- a/src/backend/storage/aio/read_stream.c
+++ b/src/backend/storage/aio/read_stream.c
@@ -776,6 +776,16 @@ read_stream_begin_impl(int flags,
 	uint32		max_possible_buffer_limit;
 	Oid			tablespace_id;
 
+	/*
+	 * Reject attempts to read non-local temporary relations; we would be
+	 * likely to get wrong data since we have no visibility into the owning
+	 * session's local buffers.
+	 */
+	if (rel && RELATION_IS_OTHER_TEMP(rel))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary relations of other sessions")));
+
 	/*
 	 * Decide how many I/Os we will allow to run at the same time.  This
 	 * number also affects how far we look ahead for opportunities to start
diff --git a/src/backend/storage/buffer/bufmgr.c b/src/backend/storage/buffer/bufmgr.c
index 3cc0b0bdd92..70353f1f8e3 100644
--- a/src/backend/storage/buffer/bufmgr.c
+++ b/src/backend/storage/buffer/bufmgr.c
@@ -795,7 +795,7 @@ PrefetchBuffer(Relation reln, ForkNumber forkNum, BlockNumber blockNum)
 		if (RELATION_IS_OTHER_TEMP(reln))
 			ereport(ERROR,
 					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-					 errmsg("cannot access temporary tables of other sessions")));
+					 errmsg("cannot access temporary relations of other sessions")));
 
 		/* pass it off to localbuf.c */
 		return PrefetchLocalBuffer(RelationGetSmgr(reln), forkNum, blockNum);
@@ -936,7 +936,7 @@ ReadBufferExtended(Relation reln, ForkNumber forkNum, BlockNumber blockNum,
 	if (RELATION_IS_OTHER_TEMP(reln))
 		ereport(ERROR,
 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-				 errmsg("cannot access temporary tables of other sessions")));
+				 errmsg("cannot access temporary relations of other sessions")));
 
 	/*
 	 * Read the buffer, and update pgstat counters to reflect a cache hit or
@@ -1292,6 +1292,12 @@ ReadBuffer_common(Relation rel, SMgrRelation smgr, char smgr_persistence,
 	int			flags;
 	char		persistence;
 
+	/* see comments in ReadBufferExtended */
+	if (rel && RELATION_IS_OTHER_TEMP(rel))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary relations of other sessions")));
+
 	/*
 	 * Backward compatibility path, most code should use ExtendBufferedRel()
 	 * instead, as acquiring the extension lock inside ExtendBufferedRel()
@@ -1382,6 +1388,12 @@ StartReadBuffersImpl(ReadBuffersOperation *operation,
 	Assert(*nblocks > 0);
 	Assert(*nblocks <= MAX_IO_COMBINE_LIMIT);
 
+	/* see comments in ReadBufferExtended */
+	if (operation->rel && RELATION_IS_OTHER_TEMP(operation->rel))
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot access temporary relations of other sessions")));
+
 	if (operation->persistence == RELPERSISTENCE_TEMP)
 	{
 		io_context = IOCONTEXT_NORMAL;
-- 
2.43.0

Reply via email to