📄 xact.c
字号:
/*------------------------------------------------------------------------- * * xact.c * top level transaction system support routines * * See src/backend/access/transam/README for more information. * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.257.2.2 2008/04/26 23:35:33 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include <time.h>#include <unistd.h>#include "access/multixact.h"#include "access/subtrans.h"#include "access/transam.h"#include "access/twophase.h"#include "access/xact.h"#include "access/xlogutils.h"#include "catalog/namespace.h"#include "commands/async.h"#include "commands/tablecmds.h"#include "commands/trigger.h"#include "executor/spi.h"#include "libpq/be-fsstubs.h"#include "miscadmin.h"#include "pgstat.h"#include "storage/fd.h"#include "storage/lmgr.h"#include "storage/procarray.h"#include "storage/sinvaladt.h"#include "storage/smgr.h"#include "utils/combocid.h"#include "utils/flatfiles.h"#include "utils/guc.h"#include "utils/inval.h"#include "utils/memutils.h"#include "utils/relcache.h"#include "utils/xml.h"/* * User-tweakable parameters */int DefaultXactIsoLevel = XACT_READ_COMMITTED;int XactIsoLevel;bool DefaultXactReadOnly = false;bool XactReadOnly;bool XactSyncCommit = true;int CommitDelay = 0; /* precommit delay in microseconds */int CommitSiblings = 5; /* # concurrent xacts needed to sleep *//* * MyXactAccessedTempRel is set when a temporary relation is accessed. * We don't allow PREPARE TRANSACTION in that case. (This is global * so that it can be set from heapam.c.) */bool MyXactAccessedTempRel = false;/* * transaction states - transaction state from server perspective */typedef enum TransState{ TRANS_DEFAULT, /* idle */ TRANS_START, /* transaction starting */ TRANS_INPROGRESS, /* inside a valid transaction */ TRANS_COMMIT, /* commit in progress */ TRANS_ABORT, /* abort in progress */ TRANS_PREPARE /* prepare in progress */} TransState;/* * transaction block states - transaction state of client queries * * Note: the subtransaction states are used only for non-topmost * transactions; the others appear only in the topmost transaction. */typedef enum TBlockState{ /* not-in-transaction-block states */ TBLOCK_DEFAULT, /* idle */ TBLOCK_STARTED, /* running single-query transaction */ /* transaction block states */ TBLOCK_BEGIN, /* starting transaction block */ TBLOCK_INPROGRESS, /* live transaction */ TBLOCK_END, /* COMMIT received */ TBLOCK_ABORT, /* failed xact, awaiting ROLLBACK */ TBLOCK_ABORT_END, /* failed xact, ROLLBACK received */ TBLOCK_ABORT_PENDING, /* live xact, ROLLBACK received */ TBLOCK_PREPARE, /* live xact, PREPARE received */ /* subtransaction states */ TBLOCK_SUBBEGIN, /* starting a subtransaction */ TBLOCK_SUBINPROGRESS, /* live subtransaction */ TBLOCK_SUBEND, /* RELEASE received */ TBLOCK_SUBABORT, /* failed subxact, awaiting ROLLBACK */ TBLOCK_SUBABORT_END, /* failed subxact, ROLLBACK received */ TBLOCK_SUBABORT_PENDING, /* live subxact, ROLLBACK received */ TBLOCK_SUBRESTART, /* live subxact, ROLLBACK TO received */ TBLOCK_SUBABORT_RESTART /* failed subxact, ROLLBACK TO received */} TBlockState;/* * transaction state structure */typedef struct TransactionStateData{ TransactionId transactionId; /* my XID, or Invalid if none */ SubTransactionId subTransactionId; /* my subxact ID */ char *name; /* savepoint name, if any */ int savepointLevel; /* savepoint level */ TransState state; /* low-level state */ TBlockState blockState; /* high-level state */ int nestingLevel; /* transaction nesting depth */ int gucNestLevel; /* GUC context nesting depth */ MemoryContext curTransactionContext; /* my xact-lifetime context */ ResourceOwner curTransactionOwner; /* my query resources */ TransactionId *childXids; /* subcommitted child XIDs, in XID order */ int nChildXids; /* # of subcommitted child XIDs */ int maxChildXids; /* allocated size of childXids[] */ Oid prevUser; /* previous CurrentUserId setting */ bool prevSecDefCxt; /* previous SecurityDefinerContext setting */ bool prevXactReadOnly; /* entry-time xact r/o state */ struct TransactionStateData *parent; /* back link to parent */} TransactionStateData;typedef TransactionStateData *TransactionState;/* * CurrentTransactionState always points to the current transaction state * block. It will point to TopTransactionStateData when not in a * transaction at all, or when in a top-level transaction. */static TransactionStateData TopTransactionStateData = { 0, /* transaction id */ 0, /* subtransaction id */ NULL, /* savepoint name */ 0, /* savepoint level */ TRANS_DEFAULT, /* transaction state */ TBLOCK_DEFAULT, /* transaction block state from the client * perspective */ 0, /* transaction nesting depth */ 0, /* GUC context nesting depth */ NULL, /* cur transaction context */ NULL, /* cur transaction resource owner */ NULL, /* subcommitted child Xids */ 0, /* # of subcommitted child Xids */ 0, /* allocated size of childXids[] */ InvalidOid, /* previous CurrentUserId setting */ false, /* previous SecurityDefinerContext setting */ false, /* entry-time xact r/o state */ NULL /* link to parent state block */};static TransactionState CurrentTransactionState = &TopTransactionStateData;/* * The subtransaction ID and command ID assignment counters are global * to a whole transaction, so we do not keep them in the state stack. */static SubTransactionId currentSubTransactionId;static CommandId currentCommandId;static bool currentCommandIdUsed;/* * xactStartTimestamp is the value of transaction_timestamp(). * stmtStartTimestamp is the value of statement_timestamp(). * xactStopTimestamp is the time at which we log a commit or abort WAL record. * These do not change as we enter and exit subtransactions, so we don't * keep them inside the TransactionState stack. */static TimestampTz xactStartTimestamp;static TimestampTz stmtStartTimestamp;static TimestampTz xactStopTimestamp;/* * GID to be used for preparing the current transaction. This is also * global to a whole transaction, so we don't keep it in the state stack. */static char *prepareGID;/* * Some commands want to force synchronous commit. */static bool forceSyncCommit = false;/* * Private context for transaction-abort work --- we reserve space for this * at startup to ensure that AbortTransaction and AbortSubTransaction can work * when we've run out of memory. */static MemoryContext TransactionAbortContext = NULL;/* * List of add-on start- and end-of-xact callbacks */typedef struct XactCallbackItem{ struct XactCallbackItem *next; XactCallback callback; void *arg;} XactCallbackItem;static XactCallbackItem *Xact_callbacks = NULL;/* * List of add-on start- and end-of-subxact callbacks */typedef struct SubXactCallbackItem{ struct SubXactCallbackItem *next; SubXactCallback callback; void *arg;} SubXactCallbackItem;static SubXactCallbackItem *SubXact_callbacks = NULL;/* local function prototypes */static void AssignTransactionId(TransactionState s);static void AbortTransaction(void);static void AtAbort_Memory(void);static void AtCleanup_Memory(void);static void AtAbort_ResourceOwner(void);static void AtCommit_LocalCache(void);static void AtCommit_Memory(void);static void AtStart_Cache(void);static void AtStart_Memory(void);static void AtStart_ResourceOwner(void);static void CallXactCallbacks(XactEvent event);static void CallSubXactCallbacks(SubXactEvent event, SubTransactionId mySubid, SubTransactionId parentSubid);static void CleanupTransaction(void);static void CommitTransaction(void);static TransactionId RecordTransactionAbort(bool isSubXact);static void StartTransaction(void);static void RecordSubTransactionCommit(void);static void StartSubTransaction(void);static void CommitSubTransaction(void);static void AbortSubTransaction(void);static void CleanupSubTransaction(void);static void PushTransaction(void);static void PopTransaction(void);static void AtSubAbort_Memory(void);static void AtSubCleanup_Memory(void);static void AtSubAbort_ResourceOwner(void);static void AtSubCommit_Memory(void);static void AtSubStart_Memory(void);static void AtSubStart_ResourceOwner(void);static void ShowTransactionState(const char *str);static void ShowTransactionStateRec(TransactionState state);static const char *BlockStateAsString(TBlockState blockState);static const char *TransStateAsString(TransState state);/* ---------------------------------------------------------------- * transaction state accessors * ---------------------------------------------------------------- *//* * IsTransactionState * * This returns true if we are inside a valid transaction; that is, * it is safe to initiate database access, take heavyweight locks, etc. */boolIsTransactionState(void){ TransactionState s = CurrentTransactionState; /* * TRANS_DEFAULT and TRANS_ABORT are obviously unsafe states. However, we * also reject the startup/shutdown states TRANS_START, TRANS_COMMIT, * TRANS_PREPARE since it might be too soon or too late within those * transition states to do anything interesting. Hence, the only "valid" * state is TRANS_INPROGRESS. */ return (s->state == TRANS_INPROGRESS);}/* * IsAbortedTransactionBlockState * * This returns true if we are currently running a query * within an aborted transaction block. */boolIsAbortedTransactionBlockState(void){ TransactionState s = CurrentTransactionState; if (s->blockState == TBLOCK_ABORT || s->blockState == TBLOCK_SUBABORT) return true; return false;}/* * GetTopTransactionId * * This will return the XID of the main transaction, assigning one if * it's not yet set. Be careful to call this only inside a valid xact. */TransactionIdGetTopTransactionId(void){ if (!TransactionIdIsValid(TopTransactionStateData.transactionId)) AssignTransactionId(&TopTransactionStateData); return TopTransactionStateData.transactionId;}/* * GetTopTransactionIdIfAny * * This will return the XID of the main transaction, if one is assigned. * It will return InvalidTransactionId if we are not currently inside a * transaction, or inside a transaction that hasn't yet been assigned an XID. */TransactionIdGetTopTransactionIdIfAny(void){ return TopTransactionStateData.transactionId;}/* * GetCurrentTransactionId * * This will return the XID of the current transaction (main or sub * transaction), assigning one if it's not yet set. Be careful to call this * only inside a valid xact. */TransactionIdGetCurrentTransactionId(void){ TransactionState s = CurrentTransactionState; if (!TransactionIdIsValid(s->transactionId)) AssignTransactionId(s); return s->transactionId;}/* * GetCurrentTransactionIdIfAny * * This will return the XID of the current sub xact, if one is assigned. * It will return InvalidTransactionId if we are not currently inside a * transaction, or inside a transaction that hasn't been assigned an XID yet. */TransactionIdGetCurrentTransactionIdIfAny(void){ return CurrentTransactionState->transactionId;}/* * AssignTransactionId * * Assigns a new permanent XID to the given TransactionState. * We do not assign XIDs to transactions until/unless this is called. * Also, any parent TransactionStates that don't yet have XIDs are assigned * one; this maintains the invariant that a child transaction has an XID * following its parent's. */static voidAssignTransactionId(TransactionState s){ bool isSubXact = (s->parent != NULL); ResourceOwner currentOwner; /* Assert that caller didn't screw up */ Assert(!TransactionIdIsValid(s->transactionId)); Assert(s->state == TRANS_INPROGRESS); /* * Ensure parent(s) have XIDs, so that a child always has an XID later * than its parent. */ if (isSubXact && !TransactionIdIsValid(s->parent->transactionId)) AssignTransactionId(s->parent); /* * Generate a new Xid and record it in PG_PROC and pg_subtrans. * * NB: we must make the subtrans entry BEFORE the Xid appears anywhere in * shared storage other than PG_PROC; because if there's no room for it in * PG_PROC, the subtrans entry is needed to ensure that other backends see * the Xid as "running". See GetNewTransactionId. */ s->transactionId = GetNewTransactionId(isSubXact); if (isSubXact) SubTransSetParent(s->transactionId, s->parent->transactionId); /* * Acquire lock on the transaction XID. (We assume this cannot block.) We * have to ensure that the lock is assigned to the transaction's own * ResourceOwner. */ currentOwner = CurrentResourceOwner; PG_TRY(); { CurrentResourceOwner = s->curTransactionOwner; XactLockTableInsert(s->transactionId); } PG_CATCH(); { /* Ensure CurrentResourceOwner is restored on error */ CurrentResourceOwner = currentOwner; PG_RE_THROW(); } PG_END_TRY(); CurrentResourceOwner = currentOwner;}/* * GetCurrentSubTransactionId */SubTransactionIdGetCurrentSubTransactionId(void){ TransactionState s = CurrentTransactionState; return s->subTransactionId;}/* * GetCurrentCommandId * * "used" must be TRUE if the caller intends to use the command ID to mark * inserted/updated/deleted tuples. FALSE means the ID is being fetched * for read-only purposes (ie, as a snapshot validity cutoff). See * CommandCounterIncrement() for discussion. */CommandIdGetCurrentCommandId(bool used){ /* this is global to a transaction, not subtransaction-local */ if (used) currentCommandIdUsed = true; return currentCommandId;}/* * GetCurrentTransactionStartTimestamp */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -