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 */
if (RelationUsesLocalBuffers(r))
MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPREL;
MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
pgstat_initstats(r);
@ -121,7 +121,7 @@ try_relation_open(Oid relationId, LOCKMODE lockmode)
/* Make note that we've accessed a temporary relation */
if (RelationUsesLocalBuffers(r))
MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPREL;
MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
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
* 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
* might still access a temp relation.
*
@ -2273,22 +2278,10 @@ PrepareTransaction(void)
* cases, such as a temp table created and dropped all within the
* 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))
ereport(ERROR,
(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

View File

@ -108,7 +108,7 @@ RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid,
*/
relpersistence = get_rel_persistence(relid);
if (relpersistence == RELPERSISTENCE_TEMP)
MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPREL;
MyXactFlags |= XACT_FLAGS_ACCESSEDTEMPNAMESPACE;
/* Check permissions. */
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
* 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);
break;
case ONCOMMIT_DROP:

View File

@ -87,10 +87,10 @@ extern int synchronous_commit;
extern int MyXactFlags;
/*
* XACT_FLAGS_ACCESSEDTEMPREL - set when a temporary relation is accessed. We
* don't allow PREPARE TRANSACTION in that case.
* XACT_FLAGS_ACCESSEDTEMPNAMESPACE - set when a temporary object is accessed.
* 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
@ -98,12 +98,6 @@ extern int MyXactFlags;
*/
#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
*/

View File

@ -148,7 +148,7 @@ SELECT create_extension_with_temp_schema();
(1 row)
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
DROP TABLE test_ext4_tab;
DROP FUNCTION create_extension_with_temp_schema();

View File

@ -310,32 +310,32 @@ 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
ERROR: cannot PREPARE a transaction that has operated on temporary objects
-- 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
ERROR: cannot PREPARE a transaction that has operated on temporary objects
-- 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
ERROR: cannot PREPARE a transaction that has operated on temporary objects
-- 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
ERROR: cannot PREPARE a transaction that has operated on temporary objects
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
ERROR: cannot PREPARE a transaction that has operated on temporary objects
begin;
create sequence pg_temp.twophase_seq;
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.
create temp table twophase_tab (a int);
begin;
@ -345,19 +345,19 @@ select a from twophase_tab;
(0 rows)
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;
insert into twophase_tab values (1);
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;
lock twophase_tab in access exclusive mode;
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;
drop table 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
-- creation is pending, so check after that. First reset the connection
-- 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)
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