mirror of
https://github.com/postgres/postgres.git
synced 2025-07-30 11:03:19 +03:00
Restrict the use of temporary namespace in two-phase transactions
Attempting to use a temporary table within a two-phase transaction is
forbidden for ages. However, there have been uncovered grounds for
a couple of other object types and commands which work on temporary
objects with two-phase commit. In short, trying to create, lock or drop
an object on a temporary schema should not be authorized within a
two-phase transaction, as it would cause its state to create
dependencies with other sessions, causing all sorts of side effects with
the existing session or other sessions spawned later on trying to use
the same temporary schema name.
Regression tests are added to cover all the grounds found, the original
report mentioned function creation, but monitoring closer there are many
other patterns with LOCK, DROP or CREATE EXTENSION which are involved.
One of the symptoms resulting in combining both is that the session
which used the temporary schema is not able to shut down completely,
waiting for being able to drop the temporary schema, something that it
cannot complete because of the two-phase transaction involved with
temporary objects. In this case the client is able to disconnect but
the session remains alive on the backend-side, potentially blocking
connection backend slots from being used. Other problems reported could
also involve server crashes.
This is back-patched down to v10, which is where 9b013dc
has introduced
MyXactFlags, something that this patch relies on.
Reported-by: Alexey Bashtanov
Author: Michael Paquier
Reviewed-by: Masahiko Sawada
Discussion: https://postgr.es/m/5d910e2e-0db8-ec06-dd5f-baec420513c3@imap.cc
Backpatch-through: 10
This commit is contained in:
@ -121,3 +121,36 @@ Objects in extension "test_ext8"
|
||||
|
||||
-- dropping it should still work
|
||||
drop extension test_ext8;
|
||||
-- Test creation of extension in temporary schema with two-phase commit,
|
||||
-- which should not work. This function wrapper is useful for portability.
|
||||
-- Avoid noise caused by CONTEXT and NOTICE messages including the temporary
|
||||
-- schema name.
|
||||
\set SHOW_CONTEXT never
|
||||
SET client_min_messages TO 'warning';
|
||||
-- First enforce presence of temporary schema.
|
||||
CREATE TEMP TABLE test_ext4_tab ();
|
||||
CREATE OR REPLACE FUNCTION create_extension_with_temp_schema()
|
||||
RETURNS VOID AS $$
|
||||
DECLARE
|
||||
tmpschema text;
|
||||
query text;
|
||||
BEGIN
|
||||
SELECT INTO tmpschema pg_my_temp_schema()::regnamespace;
|
||||
query := 'CREATE EXTENSION test_ext4 SCHEMA ' || tmpschema || ' CASCADE;';
|
||||
RAISE NOTICE 'query %', query;
|
||||
EXECUTE query;
|
||||
END; $$ LANGUAGE plpgsql;
|
||||
BEGIN;
|
||||
SELECT create_extension_with_temp_schema();
|
||||
create_extension_with_temp_schema
|
||||
-----------------------------------
|
||||
|
||||
(1 row)
|
||||
|
||||
PREPARE TRANSACTION 'twophase_extension';
|
||||
ERROR: cannot PREPARE a transaction that has operated on temporary namespace
|
||||
-- Clean up
|
||||
DROP TABLE test_ext4_tab;
|
||||
DROP FUNCTION create_extension_with_temp_schema();
|
||||
RESET client_min_messages;
|
||||
\unset SHOW_CONTEXT
|
||||
|
@ -64,3 +64,32 @@ end';
|
||||
|
||||
-- dropping it should still work
|
||||
drop extension test_ext8;
|
||||
|
||||
-- Test creation of extension in temporary schema with two-phase commit,
|
||||
-- which should not work. This function wrapper is useful for portability.
|
||||
|
||||
-- Avoid noise caused by CONTEXT and NOTICE messages including the temporary
|
||||
-- schema name.
|
||||
\set SHOW_CONTEXT never
|
||||
SET client_min_messages TO 'warning';
|
||||
-- First enforce presence of temporary schema.
|
||||
CREATE TEMP TABLE test_ext4_tab ();
|
||||
CREATE OR REPLACE FUNCTION create_extension_with_temp_schema()
|
||||
RETURNS VOID AS $$
|
||||
DECLARE
|
||||
tmpschema text;
|
||||
query text;
|
||||
BEGIN
|
||||
SELECT INTO tmpschema pg_my_temp_schema()::regnamespace;
|
||||
query := 'CREATE EXTENSION test_ext4 SCHEMA ' || tmpschema || ' CASCADE;';
|
||||
RAISE NOTICE 'query %', query;
|
||||
EXECUTE query;
|
||||
END; $$ LANGUAGE plpgsql;
|
||||
BEGIN;
|
||||
SELECT create_extension_with_temp_schema();
|
||||
PREPARE TRANSACTION 'twophase_extension';
|
||||
-- Clean up
|
||||
DROP TABLE test_ext4_tab;
|
||||
DROP FUNCTION create_extension_with_temp_schema();
|
||||
RESET client_min_messages;
|
||||
\unset SHOW_CONTEXT
|
||||
|
@ -301,3 +301,74 @@ select relname from pg_class where relname like 'temp_inh_oncommit_test%';
|
||||
(1 row)
|
||||
|
||||
drop table temp_inh_oncommit_test;
|
||||
-- Tests with two-phase commit
|
||||
-- Transactions creating objects in a temporary namespace cannot be used
|
||||
-- with two-phase commit.
|
||||
-- These cases generate errors about temporary namespace.
|
||||
-- Function creation
|
||||
begin;
|
||||
create function pg_temp.twophase_func() returns void as
|
||||
$$ select '2pc_func'::text $$ language sql;
|
||||
prepare transaction 'twophase_func';
|
||||
ERROR: cannot PREPARE a transaction that has operated on temporary namespace
|
||||
-- Function drop
|
||||
create function pg_temp.twophase_func() returns void as
|
||||
$$ select '2pc_func'::text $$ language sql;
|
||||
begin;
|
||||
drop function pg_temp.twophase_func();
|
||||
prepare transaction 'twophase_func';
|
||||
ERROR: cannot PREPARE a transaction that has operated on temporary namespace
|
||||
-- Operator creation
|
||||
begin;
|
||||
create operator pg_temp.@@ (leftarg = int4, rightarg = int4, procedure = int4mi);
|
||||
prepare transaction 'twophase_operator';
|
||||
ERROR: cannot PREPARE a transaction that has operated on temporary namespace
|
||||
-- These generate errors about temporary tables.
|
||||
begin;
|
||||
create type pg_temp.twophase_type as (a int);
|
||||
prepare transaction 'twophase_type';
|
||||
ERROR: cannot PREPARE a transaction that has operated on temporary tables
|
||||
begin;
|
||||
create view pg_temp.twophase_view as select 1;
|
||||
prepare transaction 'twophase_view';
|
||||
ERROR: cannot PREPARE a transaction that has operated on temporary tables
|
||||
begin;
|
||||
create sequence pg_temp.twophase_seq;
|
||||
prepare transaction 'twophase_sequence';
|
||||
ERROR: cannot PREPARE a transaction that has operated on temporary tables
|
||||
-- Temporary tables cannot be used with two-phase commit.
|
||||
create temp table twophase_tab (a int);
|
||||
begin;
|
||||
select a from twophase_tab;
|
||||
a
|
||||
---
|
||||
(0 rows)
|
||||
|
||||
prepare transaction 'twophase_tab';
|
||||
ERROR: cannot PREPARE a transaction that has operated on temporary tables
|
||||
begin;
|
||||
insert into twophase_tab values (1);
|
||||
prepare transaction 'twophase_tab';
|
||||
ERROR: cannot PREPARE a transaction that has operated on temporary tables
|
||||
begin;
|
||||
lock twophase_tab in access exclusive mode;
|
||||
prepare transaction 'twophase_tab';
|
||||
ERROR: cannot PREPARE a transaction that has operated on temporary tables
|
||||
begin;
|
||||
drop table twophase_tab;
|
||||
prepare transaction 'twophase_tab';
|
||||
ERROR: cannot PREPARE a transaction that has operated on temporary tables
|
||||
-- Corner case: current_schema may create a temporary schema if namespace
|
||||
-- creation is pending, so check after that. First reset the connection
|
||||
-- to remove the temporary namespace.
|
||||
\c -
|
||||
SET search_path TO 'pg_temp';
|
||||
BEGIN;
|
||||
SELECT current_schema() ~ 'pg_temp' AS is_temp_schema;
|
||||
is_temp_schema
|
||||
----------------
|
||||
t
|
||||
(1 row)
|
||||
|
||||
PREPARE TRANSACTION 'twophase_search';
|
||||
ERROR: cannot PREPARE a transaction that has operated on temporary namespace
|
||||
|
@ -224,3 +224,59 @@ select * from temp_inh_oncommit_test;
|
||||
-- one relation remains
|
||||
select relname from pg_class where relname like 'temp_inh_oncommit_test%';
|
||||
drop table temp_inh_oncommit_test;
|
||||
|
||||
-- Tests with two-phase commit
|
||||
-- Transactions creating objects in a temporary namespace cannot be used
|
||||
-- with two-phase commit.
|
||||
|
||||
-- These cases generate errors about temporary namespace.
|
||||
-- Function creation
|
||||
begin;
|
||||
create function pg_temp.twophase_func() returns void as
|
||||
$$ select '2pc_func'::text $$ language sql;
|
||||
prepare transaction 'twophase_func';
|
||||
-- Function drop
|
||||
create function pg_temp.twophase_func() returns void as
|
||||
$$ select '2pc_func'::text $$ language sql;
|
||||
begin;
|
||||
drop function pg_temp.twophase_func();
|
||||
prepare transaction 'twophase_func';
|
||||
-- Operator creation
|
||||
begin;
|
||||
create operator pg_temp.@@ (leftarg = int4, rightarg = int4, procedure = int4mi);
|
||||
prepare transaction 'twophase_operator';
|
||||
|
||||
-- These generate errors about temporary tables.
|
||||
begin;
|
||||
create type pg_temp.twophase_type as (a int);
|
||||
prepare transaction 'twophase_type';
|
||||
begin;
|
||||
create view pg_temp.twophase_view as select 1;
|
||||
prepare transaction 'twophase_view';
|
||||
begin;
|
||||
create sequence pg_temp.twophase_seq;
|
||||
prepare transaction 'twophase_sequence';
|
||||
|
||||
-- Temporary tables cannot be used with two-phase commit.
|
||||
create temp table twophase_tab (a int);
|
||||
begin;
|
||||
select a from twophase_tab;
|
||||
prepare transaction 'twophase_tab';
|
||||
begin;
|
||||
insert into twophase_tab values (1);
|
||||
prepare transaction 'twophase_tab';
|
||||
begin;
|
||||
lock twophase_tab in access exclusive mode;
|
||||
prepare transaction 'twophase_tab';
|
||||
begin;
|
||||
drop table twophase_tab;
|
||||
prepare transaction 'twophase_tab';
|
||||
|
||||
-- Corner case: current_schema may create a temporary schema if namespace
|
||||
-- creation is pending, so check after that. First reset the connection
|
||||
-- to remove the temporary namespace.
|
||||
\c -
|
||||
SET search_path TO 'pg_temp';
|
||||
BEGIN;
|
||||
SELECT current_schema() ~ 'pg_temp' AS is_temp_schema;
|
||||
PREPARE TRANSACTION 'twophase_search';
|
||||
|
Reference in New Issue
Block a user