diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c index 5a48df0fc5e..46f188d4731 100644 --- a/src/backend/catalog/namespace.c +++ b/src/backend/catalog/namespace.c @@ -4300,7 +4300,15 @@ RemoveTempRelationsCallback(int code, Datum arg) /* Need to ensure we have a usable transaction. */ AbortOutOfAnyTransaction(); StartTransactionCommand(); - PushActiveSnapshot(GetTransactionSnapshot()); + + /* + * Need an active snapshot for toast fetches during deletion. Do not + * use GetTransactionSnapshot(): under SERIALIZABLE READ ONLY + * DEFERRABLE it may wait in GetSafeSnapshot(), and proc_exit holds + * off interrupts so that wait cannot be cancelled. A catalog + * snapshot is enough and avoids that path. + */ + PushActiveSnapshot(GetCatalogSnapshot(RelationRelationId)); RemoveTempRelations(myTempNamespace); diff --git a/src/test/modules/test_misc/t/015_temp_schema_exit_deferrable.pl b/src/test/modules/test_misc/t/015_temp_schema_exit_deferrable.pl new file mode 100644 index 00000000000..07de36630a6 --- /dev/null +++ b/src/test/modules/test_misc/t/015_temp_schema_exit_deferrable.pl @@ -0,0 +1,58 @@ +# Copyright (c) 2026, PostgreSQL Global Development Group + +# Backend exit must finish temp-schema cleanup even when the session default +# is SERIALIZABLE READ ONLY DEFERRABLE and a prepared serializable transaction +# is still around. + +use strict; +use warnings FATAL => 'all'; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +my $node = PostgreSQL::Test::Cluster->new('deferrable_temp_exit'); +$node->init; +$node->append_conf('postgresql.conf', 'max_prepared_transactions = 1'); +$node->start; + +my $psql1 = $node->background_psql('postgres'); + +$psql1->query_safe( + q{ +CREATE TEMPORARY TABLE tt (i int); +SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY DEFERRABLE; +}); + +my $pid = $psql1->query_safe(q{SELECT pg_backend_pid();}); +chomp $pid; +like($pid, qr/^\d+$/, "backend pid $pid"); + +# Overlapping read/write serializable xact that outlives session 1. +$node->safe_psql( + 'postgres', + q{ +CREATE TABLE t (i int); +BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE; +INSERT INTO t VALUES (1); +PREPARE TRANSACTION 'pt'; +}); + +# Disconnect: RemoveTempRelationsCallback runs during backend exit. +$psql1->quit; + +ok( $node->poll_query_until( + 'postgres', + "SELECT count(*) = 0 FROM pg_stat_activity WHERE pid = $pid"), + 'backend exited despite prepared serializable xact'); + +is( $node->safe_psql( + 'postgres', + q{SELECT count(*) FROM pg_class WHERE relname = 'tt' AND relpersistence = 't'} + ), + '0', + 'temporary table cleaned up on exit'); + +$node->safe_psql('postgres', q{ROLLBACK PREPARED 'pt';}); +$node->safe_psql('postgres', q{DROP TABLE t;}); + +done_testing();