mirror of
https://github.com/postgres/postgres.git
synced 2026-04-28 17:49:35 -04:00
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
413 lines
13 KiB
C
413 lines
13 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* xact.h
|
|
* postgres transaction system definitions
|
|
*
|
|
*
|
|
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
* src/include/access/xact.h
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#ifndef XACT_H
|
|
#define XACT_H
|
|
|
|
#include "access/xlogreader.h"
|
|
#include "lib/stringinfo.h"
|
|
#include "nodes/pg_list.h"
|
|
#include "storage/relfilenode.h"
|
|
#include "storage/sinval.h"
|
|
#include "utils/datetime.h"
|
|
|
|
|
|
/*
|
|
* Xact isolation levels
|
|
*/
|
|
#define XACT_READ_UNCOMMITTED 0
|
|
#define XACT_READ_COMMITTED 1
|
|
#define XACT_REPEATABLE_READ 2
|
|
#define XACT_SERIALIZABLE 3
|
|
|
|
extern int DefaultXactIsoLevel;
|
|
extern PGDLLIMPORT int XactIsoLevel;
|
|
|
|
/*
|
|
* We implement three isolation levels internally.
|
|
* The two stronger ones use one snapshot per database transaction;
|
|
* the others use one snapshot per statement.
|
|
* Serializable uses predicate locks in addition to snapshots.
|
|
* These macros should be used to check which isolation level is selected.
|
|
*/
|
|
#define IsolationUsesXactSnapshot() (XactIsoLevel >= XACT_REPEATABLE_READ)
|
|
#define IsolationIsSerializable() (XactIsoLevel == XACT_SERIALIZABLE)
|
|
|
|
/* Xact read-only state */
|
|
extern bool DefaultXactReadOnly;
|
|
extern bool XactReadOnly;
|
|
|
|
/*
|
|
* Xact is deferrable -- only meaningful (currently) for read only
|
|
* SERIALIZABLE transactions
|
|
*/
|
|
extern bool DefaultXactDeferrable;
|
|
extern bool XactDeferrable;
|
|
|
|
typedef enum
|
|
{
|
|
SYNCHRONOUS_COMMIT_OFF, /* asynchronous commit */
|
|
SYNCHRONOUS_COMMIT_LOCAL_FLUSH, /* wait for local flush only */
|
|
SYNCHRONOUS_COMMIT_REMOTE_WRITE, /* wait for local flush and remote
|
|
* write */
|
|
SYNCHRONOUS_COMMIT_REMOTE_FLUSH, /* wait for local and remote flush */
|
|
SYNCHRONOUS_COMMIT_REMOTE_APPLY /* wait for local flush and remote apply */
|
|
} SyncCommitLevel;
|
|
|
|
/* Define the default setting for synchronous_commit */
|
|
#define SYNCHRONOUS_COMMIT_ON SYNCHRONOUS_COMMIT_REMOTE_FLUSH
|
|
|
|
/* Synchronous commit level */
|
|
extern int synchronous_commit;
|
|
|
|
/*
|
|
* Miscellaneous flag bits to record events which occur on the top level
|
|
* transaction. These flags are only persisted in MyXactFlags and are intended
|
|
* so we remember to do certain things later in the transaction. This is
|
|
* globally accessible, so can be set from anywhere in the code which requires
|
|
* recording flags.
|
|
*/
|
|
extern int MyXactFlags;
|
|
|
|
/*
|
|
* XACT_FLAGS_ACCESSEDTEMPREL - set when a temporary relation is accessed. We
|
|
* don't allow PREPARE TRANSACTION in that case.
|
|
*/
|
|
#define XACT_FLAGS_ACCESSEDTEMPREL (1U << 0)
|
|
|
|
/*
|
|
* XACT_FLAGS_ACQUIREDACCESSEXCLUSIVELOCK - records whether the top level xact
|
|
* logged any Access Exclusive Locks.
|
|
*/
|
|
#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
|
|
*/
|
|
typedef enum
|
|
{
|
|
XACT_EVENT_COMMIT,
|
|
XACT_EVENT_PARALLEL_COMMIT,
|
|
XACT_EVENT_ABORT,
|
|
XACT_EVENT_PARALLEL_ABORT,
|
|
XACT_EVENT_PREPARE,
|
|
XACT_EVENT_PRE_COMMIT,
|
|
XACT_EVENT_PARALLEL_PRE_COMMIT,
|
|
XACT_EVENT_PRE_PREPARE
|
|
} XactEvent;
|
|
|
|
typedef void (*XactCallback) (XactEvent event, void *arg);
|
|
|
|
typedef enum
|
|
{
|
|
SUBXACT_EVENT_START_SUB,
|
|
SUBXACT_EVENT_COMMIT_SUB,
|
|
SUBXACT_EVENT_ABORT_SUB,
|
|
SUBXACT_EVENT_PRE_COMMIT_SUB
|
|
} SubXactEvent;
|
|
|
|
typedef void (*SubXactCallback) (SubXactEvent event, SubTransactionId mySubid,
|
|
SubTransactionId parentSubid, void *arg);
|
|
|
|
|
|
/* ----------------
|
|
* transaction-related XLOG entries
|
|
* ----------------
|
|
*/
|
|
|
|
/*
|
|
* XLOG allows to store some information in high 4 bits of log record xl_info
|
|
* field. We use 3 for the opcode, and one about an optional flag variable.
|
|
*/
|
|
#define XLOG_XACT_COMMIT 0x00
|
|
#define XLOG_XACT_PREPARE 0x10
|
|
#define XLOG_XACT_ABORT 0x20
|
|
#define XLOG_XACT_COMMIT_PREPARED 0x30
|
|
#define XLOG_XACT_ABORT_PREPARED 0x40
|
|
#define XLOG_XACT_ASSIGNMENT 0x50
|
|
/* free opcode 0x60 */
|
|
/* free opcode 0x70 */
|
|
|
|
/* mask for filtering opcodes out of xl_info */
|
|
#define XLOG_XACT_OPMASK 0x70
|
|
|
|
/* does this record have a 'xinfo' field or not */
|
|
#define XLOG_XACT_HAS_INFO 0x80
|
|
|
|
/*
|
|
* The following flags, stored in xinfo, determine which information is
|
|
* contained in commit/abort records.
|
|
*/
|
|
#define XACT_XINFO_HAS_DBINFO (1U << 0)
|
|
#define XACT_XINFO_HAS_SUBXACTS (1U << 1)
|
|
#define XACT_XINFO_HAS_RELFILENODES (1U << 2)
|
|
#define XACT_XINFO_HAS_INVALS (1U << 3)
|
|
#define XACT_XINFO_HAS_TWOPHASE (1U << 4)
|
|
#define XACT_XINFO_HAS_ORIGIN (1U << 5)
|
|
#define XACT_XINFO_HAS_AE_LOCKS (1U << 6)
|
|
|
|
/*
|
|
* Also stored in xinfo, these indicating a variety of additional actions that
|
|
* need to occur when emulating transaction effects during recovery.
|
|
*
|
|
* They are named XactCompletion... to differentiate them from
|
|
* EOXact... routines which run at the end of the original transaction
|
|
* completion.
|
|
*/
|
|
#define XACT_COMPLETION_APPLY_FEEDBACK (1U << 29)
|
|
#define XACT_COMPLETION_UPDATE_RELCACHE_FILE (1U << 30)
|
|
#define XACT_COMPLETION_FORCE_SYNC_COMMIT (1U << 31)
|
|
|
|
/* Access macros for above flags */
|
|
#define XactCompletionApplyFeedback(xinfo) \
|
|
((xinfo & XACT_COMPLETION_APPLY_FEEDBACK) != 0)
|
|
#define XactCompletionRelcacheInitFileInval(xinfo) \
|
|
((xinfo & XACT_COMPLETION_UPDATE_RELCACHE_FILE) != 0)
|
|
#define XactCompletionForceSyncCommit(xinfo) \
|
|
((xinfo & XACT_COMPLETION_FORCE_SYNC_COMMIT) != 0)
|
|
|
|
typedef struct xl_xact_assignment
|
|
{
|
|
TransactionId xtop; /* assigned XID's top-level XID */
|
|
int nsubxacts; /* number of subtransaction XIDs */
|
|
TransactionId xsub[FLEXIBLE_ARRAY_MEMBER]; /* assigned subxids */
|
|
} xl_xact_assignment;
|
|
|
|
#define MinSizeOfXactAssignment offsetof(xl_xact_assignment, xsub)
|
|
|
|
/*
|
|
* Commit and abort records can contain a lot of information. But a large
|
|
* portion of the records won't need all possible pieces of information. So we
|
|
* only include what's needed.
|
|
*
|
|
* A minimal commit/abort record only consists of a xl_xact_commit/abort
|
|
* struct. The presence of additional information is indicated by bits set in
|
|
* 'xl_xact_xinfo->xinfo'. The presence of the xinfo field itself is signalled
|
|
* by a set XLOG_XACT_HAS_INFO bit in the xl_info field.
|
|
*
|
|
* NB: All the individual data chunks should be sized to multiples of
|
|
* sizeof(int) and only require int32 alignment. If they require bigger
|
|
* alignment, they need to be copied upon reading.
|
|
*/
|
|
|
|
/* sub-records for commit/abort */
|
|
|
|
typedef struct xl_xact_xinfo
|
|
{
|
|
/*
|
|
* Even though we right now only require 1 byte of space in xinfo we use
|
|
* four so following records don't have to care about alignment. Commit
|
|
* records can be large, so copying large portions isn't attractive.
|
|
*/
|
|
uint32 xinfo;
|
|
} xl_xact_xinfo;
|
|
|
|
typedef struct xl_xact_dbinfo
|
|
{
|
|
Oid dbId; /* MyDatabaseId */
|
|
Oid tsId; /* MyDatabaseTableSpace */
|
|
} xl_xact_dbinfo;
|
|
|
|
typedef struct xl_xact_subxacts
|
|
{
|
|
int nsubxacts; /* number of subtransaction XIDs */
|
|
TransactionId subxacts[FLEXIBLE_ARRAY_MEMBER];
|
|
} xl_xact_subxacts;
|
|
#define MinSizeOfXactSubxacts offsetof(xl_xact_subxacts, subxacts)
|
|
|
|
typedef struct xl_xact_relfilenodes
|
|
{
|
|
int nrels; /* number of subtransaction XIDs */
|
|
RelFileNode xnodes[FLEXIBLE_ARRAY_MEMBER];
|
|
} xl_xact_relfilenodes;
|
|
#define MinSizeOfXactRelfilenodes offsetof(xl_xact_relfilenodes, xnodes)
|
|
|
|
typedef struct xl_xact_invals
|
|
{
|
|
int nmsgs; /* number of shared inval msgs */
|
|
SharedInvalidationMessage msgs[FLEXIBLE_ARRAY_MEMBER];
|
|
} xl_xact_invals;
|
|
#define MinSizeOfXactInvals offsetof(xl_xact_invals, msgs)
|
|
|
|
typedef struct xl_xact_twophase
|
|
{
|
|
TransactionId xid;
|
|
} xl_xact_twophase;
|
|
|
|
typedef struct xl_xact_origin
|
|
{
|
|
XLogRecPtr origin_lsn;
|
|
TimestampTz origin_timestamp;
|
|
} xl_xact_origin;
|
|
|
|
typedef struct xl_xact_commit
|
|
{
|
|
TimestampTz xact_time; /* time of commit */
|
|
|
|
/* xl_xact_xinfo follows if XLOG_XACT_HAS_INFO */
|
|
/* xl_xact_dbinfo follows if XINFO_HAS_DBINFO */
|
|
/* xl_xact_subxacts follows if XINFO_HAS_SUBXACT */
|
|
/* xl_xact_relfilenodes follows if XINFO_HAS_RELFILENODES */
|
|
/* xl_xact_invals follows if XINFO_HAS_INVALS */
|
|
/* xl_xact_twophase follows if XINFO_HAS_TWOPHASE */
|
|
/* xl_xact_origin follows if XINFO_HAS_ORIGIN, stored unaligned! */
|
|
} xl_xact_commit;
|
|
#define MinSizeOfXactCommit (offsetof(xl_xact_commit, xact_time) + sizeof(TimestampTz))
|
|
|
|
typedef struct xl_xact_abort
|
|
{
|
|
TimestampTz xact_time; /* time of abort */
|
|
|
|
/* xl_xact_xinfo follows if XLOG_XACT_HAS_INFO */
|
|
/* No db_info required */
|
|
/* xl_xact_subxacts follows if HAS_SUBXACT */
|
|
/* xl_xact_relfilenodes follows if HAS_RELFILENODES */
|
|
/* No invalidation messages needed. */
|
|
/* xl_xact_twophase follows if XINFO_HAS_TWOPHASE */
|
|
} xl_xact_abort;
|
|
#define MinSizeOfXactAbort sizeof(xl_xact_abort)
|
|
|
|
/*
|
|
* Commit/Abort records in the above form are a bit verbose to parse, so
|
|
* there's a deconstructed versions generated by ParseCommit/AbortRecord() for
|
|
* easier consumption.
|
|
*/
|
|
typedef struct xl_xact_parsed_commit
|
|
{
|
|
TimestampTz xact_time;
|
|
|
|
uint32 xinfo;
|
|
|
|
Oid dbId; /* MyDatabaseId */
|
|
Oid tsId; /* MyDatabaseTableSpace */
|
|
|
|
int nsubxacts;
|
|
TransactionId *subxacts;
|
|
|
|
int nrels;
|
|
RelFileNode *xnodes;
|
|
|
|
int nmsgs;
|
|
SharedInvalidationMessage *msgs;
|
|
|
|
TransactionId twophase_xid; /* only for 2PC */
|
|
|
|
XLogRecPtr origin_lsn;
|
|
TimestampTz origin_timestamp;
|
|
} xl_xact_parsed_commit;
|
|
|
|
typedef struct xl_xact_parsed_abort
|
|
{
|
|
TimestampTz xact_time;
|
|
uint32 xinfo;
|
|
|
|
int nsubxacts;
|
|
TransactionId *subxacts;
|
|
|
|
int nrels;
|
|
RelFileNode *xnodes;
|
|
|
|
TransactionId twophase_xid; /* only for 2PC */
|
|
} xl_xact_parsed_abort;
|
|
|
|
|
|
/* ----------------
|
|
* extern definitions
|
|
* ----------------
|
|
*/
|
|
extern bool IsTransactionState(void);
|
|
extern bool IsAbortedTransactionBlockState(void);
|
|
extern TransactionId GetTopTransactionId(void);
|
|
extern TransactionId GetTopTransactionIdIfAny(void);
|
|
extern TransactionId GetCurrentTransactionId(void);
|
|
extern TransactionId GetCurrentTransactionIdIfAny(void);
|
|
extern TransactionId GetStableLatestTransactionId(void);
|
|
extern SubTransactionId GetCurrentSubTransactionId(void);
|
|
extern void MarkCurrentTransactionIdLoggedIfAny(void);
|
|
extern bool SubTransactionIsActive(SubTransactionId subxid);
|
|
extern CommandId GetCurrentCommandId(bool used);
|
|
extern void SetParallelStartTimestamps(TimestampTz xact_ts, TimestampTz stmt_ts);
|
|
extern TimestampTz GetCurrentTransactionStartTimestamp(void);
|
|
extern TimestampTz GetCurrentStatementStartTimestamp(void);
|
|
extern TimestampTz GetCurrentTransactionStopTimestamp(void);
|
|
extern void SetCurrentStatementStartTimestamp(void);
|
|
extern int GetCurrentTransactionNestLevel(void);
|
|
extern bool TransactionIdIsCurrentTransactionId(TransactionId xid);
|
|
extern void CommandCounterIncrement(void);
|
|
extern void ForceSyncCommit(void);
|
|
extern void StartTransactionCommand(void);
|
|
extern void CommitTransactionCommand(void);
|
|
extern void AbortCurrentTransaction(void);
|
|
extern void BeginTransactionBlock(void);
|
|
extern bool EndTransactionBlock(void);
|
|
extern bool PrepareTransactionBlock(char *gid);
|
|
extern void UserAbortTransactionBlock(void);
|
|
extern void ReleaseSavepoint(List *options);
|
|
extern void DefineSavepoint(char *name);
|
|
extern void RollbackToSavepoint(List *options);
|
|
extern void BeginInternalSubTransaction(char *name);
|
|
extern void ReleaseCurrentSubTransaction(void);
|
|
extern void RollbackAndReleaseCurrentSubTransaction(void);
|
|
extern bool IsSubTransaction(void);
|
|
extern Size EstimateTransactionStateSpace(void);
|
|
extern void SerializeTransactionState(Size maxsize, char *start_address);
|
|
extern void StartParallelWorkerTransaction(char *tstatespace);
|
|
extern void EndParallelWorkerTransaction(void);
|
|
extern bool IsTransactionBlock(void);
|
|
extern bool IsTransactionOrTransactionBlock(void);
|
|
extern char TransactionBlockStatusCode(void);
|
|
extern void AbortOutOfAnyTransaction(void);
|
|
extern void PreventTransactionChain(bool isTopLevel, const char *stmtType);
|
|
extern void RequireTransactionChain(bool isTopLevel, const char *stmtType);
|
|
extern void WarnNoTransactionChain(bool isTopLevel, const char *stmtType);
|
|
extern bool IsInTransactionChain(bool isTopLevel);
|
|
extern void RegisterXactCallback(XactCallback callback, void *arg);
|
|
extern void UnregisterXactCallback(XactCallback callback, void *arg);
|
|
extern void RegisterSubXactCallback(SubXactCallback callback, void *arg);
|
|
extern void UnregisterSubXactCallback(SubXactCallback callback, void *arg);
|
|
|
|
extern int xactGetCommittedChildren(TransactionId **ptr);
|
|
|
|
extern XLogRecPtr XactLogCommitRecord(TimestampTz commit_time,
|
|
int nsubxacts, TransactionId *subxacts,
|
|
int nrels, RelFileNode *rels,
|
|
int nmsgs, SharedInvalidationMessage *msgs,
|
|
bool relcacheInval, bool forceSync,
|
|
int xactflags,
|
|
TransactionId twophase_xid);
|
|
|
|
extern XLogRecPtr XactLogAbortRecord(TimestampTz abort_time,
|
|
int nsubxacts, TransactionId *subxacts,
|
|
int nrels, RelFileNode *rels,
|
|
int xactflags, TransactionId twophase_xid);
|
|
extern void xact_redo(XLogReaderState *record);
|
|
|
|
/* xactdesc.c */
|
|
extern void xact_desc(StringInfo buf, XLogReaderState *record);
|
|
extern const char *xact_identify(uint8 info);
|
|
|
|
/* also in xactdesc.c, so they can be shared between front/backend code */
|
|
extern void ParseCommitRecord(uint8 info, xl_xact_commit *xlrec, xl_xact_parsed_commit *parsed);
|
|
extern void ParseAbortRecord(uint8 info, xl_xact_abort *xlrec, xl_xact_parsed_abort *parsed);
|
|
|
|
extern void EnterParallelMode(void);
|
|
extern void ExitParallelMode(void);
|
|
extern bool IsInParallelMode(void);
|
|
|
|
#endif /* XACT_H */
|