Simplify restriction handling of two-phase commit for temporary objects

There were two flags used to track the access to temporary tables and
to the temporary namespace of a session which are used to restrict
PREPARE TRANSACTION, however the first control flag is a concept
included in the second.  This removes the flag for temporary table
tracking, keeping around only the one at namespace level.

Author: Michael Paquier
Reviewed-by: Álvaro Herrera
Discussion: https://postgr.es/m/20190118053126.GH1883@paquier.xyz
This commit is contained in:
Michael Paquier 2019-01-26 10:45:23 +09:00
parent df4c904440
commit c9b75c5838
7 changed files with 25 additions and 38 deletions

View File

@ -71,7 +71,7 @@ relation_open(Oid relationId, LOCKMODE lockmode)
/* Make note that we've accessed a temporary relation */ /* Make note that we've accessed a temporary relation */
if (RelationUsesLocalBuffers(r)) if (RelationUsesLocalBuffers(r))
MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPREL; MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
pgstat_initstats(r); pgstat_initstats(r);
@ -121,7 +121,7 @@ try_relation_open(Oid relationId, LOCKMODE lockmode)
/* Make note that we've accessed a temporary relation */ /* Make note that we've accessed a temporary relation */
if (RelationUsesLocalBuffers(r)) if (RelationUsesLocalBuffers(r))
MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPREL; MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
pgstat_initstats(r); pgstat_initstats(r);

View File

@ -2266,6 +2266,11 @@ PrepareTransaction(void)
* clean up the source backend's local buffers and ON COMMIT state if the * clean up the source backend's local buffers and ON COMMIT state if the
* prepared xact includes a DROP of a temp table. * prepared xact includes a DROP of a temp table.
* *
* Other objects types, like functions, operators or extensions, share the
* same restriction as they should not be created, locked or dropped as
* this can mess up with this session or even a follow-up session trying
* to use the same temporary namespace.
*
* We must check this after executing any ON COMMIT actions, because they * We must check this after executing any ON COMMIT actions, because they
* might still access a temp relation. * might still access a temp relation.
* *
@ -2273,22 +2278,10 @@ PrepareTransaction(void)
* cases, such as a temp table created and dropped all within the * cases, such as a temp table created and dropped all within the
* transaction. That seems to require much more bookkeeping though. * transaction. That seems to require much more bookkeeping though.
*/ */
if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPREL))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot PREPARE a transaction that has operated on temporary tables")));
/*
* Similarly, PREPARE TRANSACTION is not allowed if the temporary
* namespace has been involved in this transaction as we cannot allow it
* to create, lock, or even drop objects within the temporary namespace
* as this can mess up with this session or even a follow-up session
* trying to use the same temporary namespace.
*/
if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE)) if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot PREPARE a transaction that has operated on temporary namespace"))); errmsg("cannot PREPARE a transaction that has operated on temporary objects")));
/* /*
* Likewise, don't allow PREPARE after pg_export_snapshot. This could be * Likewise, don't allow PREPARE after pg_export_snapshot. This could be

View File

@ -108,7 +108,7 @@ RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid,
*/ */
relpersistence = get_rel_persistence(relid); relpersistence = get_rel_persistence(relid);
if (relpersistence == RELPERSISTENCE_TEMP) if (relpersistence == RELPERSISTENCE_TEMP)
MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPREL; MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
/* Check permissions. */ /* Check permissions. */
aclresult = LockTableAclCheck(relid, lockmode, GetUserId()); aclresult = LockTableAclCheck(relid, lockmode, GetUserId());

View File

@ -13642,7 +13642,7 @@ PreCommit_on_commit_actions(void)
* relations, we can skip truncating ON COMMIT DELETE ROWS * relations, we can skip truncating ON COMMIT DELETE ROWS
* tables, as they must still be empty. * tables, as they must still be empty.
*/ */
if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPREL)) if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid); oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
break; break;
case ONCOMMIT_DROP: case ONCOMMIT_DROP:

View File

@ -87,10 +87,10 @@ extern int synchronous_commit;
extern int MyXactFlags; extern int MyXactFlags;
/* /*
* XACT_FLAGS_ACCESSEDTEMPREL - set when a temporary relation is accessed. We * XACT_FLAGS_ACCESSEDTEMPNAMESPACE - set when a temporary object is accessed.
* don't allow PREPARE TRANSACTION in that case. * We don't allow PREPARE TRANSACTION in that case.
*/ */
#define XACT_FLAGS_ACCESSEDTEMPREL (1U << 0) #define XACT_FLAGS_ACCESSEDTEMPNAMESPACE (1U << 0)
/* /*
* XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK - records whether the top level xact * XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK - records whether the top level xact
@ -98,12 +98,6 @@ extern int MyXactFlags;
*/ */
#define XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK (1U << 1) #define XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK (1U << 1)
/*
* XACT_FLAGS_ACCESSEDTEMPNAMESPACE - set when a temporary namespace is
* accessed. We don't allow PREPARE TRANSACTION in that case.
*/
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE (1U << 2)
/* /*
* start- and end-of-transaction callbacks for dynamically loaded modules * start- and end-of-transaction callbacks for dynamically loaded modules
*/ */

View File

@ -148,7 +148,7 @@ SELECT create_extension_with_temp_schema();
(1 row) (1 row)
PREPARE TRANSACTION 'twophase_extension'; PREPARE TRANSACTION 'twophase_extension';
ERROR: cannot PREPARE a transaction that has operated on temporary namespace ERROR: cannot PREPARE a transaction that has operated on temporary objects
-- Clean up -- Clean up
DROP TABLE test_ext4_tab; DROP TABLE test_ext4_tab;
DROP FUNCTION create_extension_with_temp_schema(); DROP FUNCTION create_extension_with_temp_schema();

View File

@ -310,32 +310,32 @@ begin;
create function pg_temp.twophase_func() returns void as create function pg_temp.twophase_func() returns void as
$$ select '2pc_func'::text $$ language sql; $$ select '2pc_func'::text $$ language sql;
prepare transaction 'twophase_func'; prepare transaction 'twophase_func';
ERROR: cannot PREPARE a transaction that has operated on temporary namespace ERROR: cannot PREPARE a transaction that has operated on temporary objects
-- Function drop -- Function drop
create function pg_temp.twophase_func() returns void as create function pg_temp.twophase_func() returns void as
$$ select '2pc_func'::text $$ language sql; $$ select '2pc_func'::text $$ language sql;
begin; begin;
drop function pg_temp.twophase_func(); drop function pg_temp.twophase_func();
prepare transaction 'twophase_func'; prepare transaction 'twophase_func';
ERROR: cannot PREPARE a transaction that has operated on temporary namespace ERROR: cannot PREPARE a transaction that has operated on temporary objects
-- Operator creation -- Operator creation
begin; begin;
create operator pg_temp.@@ (leftarg = int4, rightarg = int4, procedure = int4mi); create operator pg_temp.@@ (leftarg = int4, rightarg = int4, procedure = int4mi);
prepare transaction 'twophase_operator'; prepare transaction 'twophase_operator';
ERROR: cannot PREPARE a transaction that has operated on temporary namespace ERROR: cannot PREPARE a transaction that has operated on temporary objects
-- These generate errors about temporary tables. -- These generate errors about temporary tables.
begin; begin;
create type pg_temp.twophase_type as (a int); create type pg_temp.twophase_type as (a int);
prepare transaction 'twophase_type'; prepare transaction 'twophase_type';
ERROR: cannot PREPARE a transaction that has operated on temporary tables ERROR: cannot PREPARE a transaction that has operated on temporary objects
begin; begin;
create view pg_temp.twophase_view as select 1; create view pg_temp.twophase_view as select 1;
prepare transaction 'twophase_view'; prepare transaction 'twophase_view';
ERROR: cannot PREPARE a transaction that has operated on temporary tables ERROR: cannot PREPARE a transaction that has operated on temporary objects
begin; begin;
create sequence pg_temp.twophase_seq; create sequence pg_temp.twophase_seq;
prepare transaction 'twophase_sequence'; prepare transaction 'twophase_sequence';
ERROR: cannot PREPARE a transaction that has operated on temporary tables ERROR: cannot PREPARE a transaction that has operated on temporary objects
-- Temporary tables cannot be used with two-phase commit. -- Temporary tables cannot be used with two-phase commit.
create temp table twophase_tab (a int); create temp table twophase_tab (a int);
begin; begin;
@ -345,19 +345,19 @@ select a from twophase_tab;
(0 rows) (0 rows)
prepare transaction 'twophase_tab'; prepare transaction 'twophase_tab';
ERROR: cannot PREPARE a transaction that has operated on temporary tables ERROR: cannot PREPARE a transaction that has operated on temporary objects
begin; begin;
insert into twophase_tab values (1); insert into twophase_tab values (1);
prepare transaction 'twophase_tab'; prepare transaction 'twophase_tab';
ERROR: cannot PREPARE a transaction that has operated on temporary tables ERROR: cannot PREPARE a transaction that has operated on temporary objects
begin; begin;
lock twophase_tab in access exclusive mode; lock twophase_tab in access exclusive mode;
prepare transaction 'twophase_tab'; prepare transaction 'twophase_tab';
ERROR: cannot PREPARE a transaction that has operated on temporary tables ERROR: cannot PREPARE a transaction that has operated on temporary objects
begin; begin;
drop table twophase_tab; drop table twophase_tab;
prepare transaction 'twophase_tab'; prepare transaction 'twophase_tab';
ERROR: cannot PREPARE a transaction that has operated on temporary tables ERROR: cannot PREPARE a transaction that has operated on temporary objects
-- Corner case: current_schema may create a temporary schema if namespace -- Corner case: current_schema may create a temporary schema if namespace
-- creation is pending, so check after that. First reset the connection -- creation is pending, so check after that. First reset the connection
-- to remove the temporary namespace, and make sure that non-parallel plans -- to remove the temporary namespace, and make sure that non-parallel plans
@ -374,4 +374,4 @@ SELECT current_schema() ~ 'pg_temp' AS is_temp_schema;
(1 row) (1 row)
PREPARE TRANSACTION 'twophase_search'; PREPARE TRANSACTION 'twophase_search';
ERROR: cannot PREPARE a transaction that has operated on temporary namespace ERROR: cannot PREPARE a transaction that has operated on temporary objects