📄 xact.c
字号:
/*------------------------------------------------------------------------- * * xact.c * top level transaction system support routines * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.156.2.1 2004/08/11 04:08:00 tgl Exp $ * * NOTES * Transaction aborts can now occur two ways: * * 1) system dies from some internal cause (syntax error, etc..) * 2) user types ABORT * * These two cases used to be treated identically, but now * we need to distinguish them. Why? consider the following * two situations: * * case 1 case 2 * ------ ------ * 1) user types BEGIN 1) user types BEGIN * 2) user does something 2) user does something * 3) user does not like what 3) system aborts for some reason * she sees and types ABORT * * In case 1, we want to abort the transaction and return to the * default state. In case 2, there may be more commands coming * our way which are part of the same transaction block and we have * to ignore these commands until we see a COMMIT transaction or * ROLLBACK. * * Internal aborts are now handled by AbortTransactionBlock(), just as * they always have been, and user aborts are now handled by * UserAbortTransactionBlock(). Both of them rely on AbortTransaction() * to do all the real work. The only difference is what state we * enter after AbortTransaction() does its work: * * * AbortTransactionBlock() leaves us in TBLOCK_ABORT and * * UserAbortTransactionBlock() leaves us in TBLOCK_ENDABORT * * Low-level transaction abort handling is divided into two phases: * * AbortTransaction() executes as soon as we realize the transaction * has failed. It should release all shared resources (locks etc) * so that we do not delay other backends unnecessarily. * * CleanupTransaction() executes when we finally see a user COMMIT * or ROLLBACK command; it cleans things up and gets us out of * the transaction internally. In particular, we mustn't destroy * TopTransactionContext until this point. * * NOTES * The essential aspects of the transaction system are: * * o transaction id generation * o transaction log updating * o memory cleanup * o cache invalidation * o lock cleanup * * Hence, the functional division of the transaction code is * based on which of the above things need to be done during * a start/commit/abort transaction. For instance, the * routine AtCommit_Memory() takes care of all the memory * cleanup stuff done at commit time. * * The code is layered as follows: * * StartTransaction * CommitTransaction * AbortTransaction * CleanupTransaction * * are provided to do the lower level work like recording * the transaction status in the log and doing memory cleanup. * above these routines are another set of functions: * * StartTransactionCommand * CommitTransactionCommand * AbortCurrentTransaction * * These are the routines used in the postgres main processing * loop. They are sensitive to the current transaction block state * and make calls to the lower level routines appropriately. * * Support for transaction blocks is provided via the functions: * * StartTransactionBlock * CommitTransactionBlock * AbortTransactionBlock * * These are invoked only in response to a user "BEGIN WORK", "COMMIT", * or "ROLLBACK" command. The tricky part about these functions * is that they are called within the postgres main loop, in between * the StartTransactionCommand() and CommitTransactionCommand(). * * For example, consider the following sequence of user commands: * * 1) begin * 2) select * from foo * 3) insert into foo (bar = baz) * 4) commit * * in the main processing loop, this results in the following * transaction sequence: * * / StartTransactionCommand(); * 1) / ProcessUtility(); << begin * \ StartTransactionBlock(); * \ CommitTransactionCommand(); * * / StartTransactionCommand(); * 2) < ProcessQuery(); << select * from foo * \ CommitTransactionCommand(); * * / StartTransactionCommand(); * 3) < ProcessQuery(); << insert into foo (bar = baz) * \ CommitTransactionCommand(); * * / StartTransactionCommand(); * 4) / ProcessUtility(); << commit * \ CommitTransactionBlock(); * \ CommitTransactionCommand(); * * The point of this example is to demonstrate the need for * StartTransactionCommand() and CommitTransactionCommand() to * be state smart -- they should do nothing in between the calls * to StartTransactionBlock() and EndTransactionBlock() and * outside these calls they need to do normal start/commit * processing. * * Furthermore, suppose the "select * from foo" caused an abort * condition. We would then want to abort the transaction and * ignore all subsequent commands up to the "commit". * -cim 3/23/90 * *------------------------------------------------------------------------- */#include "postgres.h"#include <unistd.h>#include <sys/time.h>#include "access/gistscan.h"#include "access/hash.h"#include "access/nbtree.h"#include "access/rtree.h"#include "access/xact.h"#include "catalog/heap.h"#include "catalog/index.h"#include "catalog/namespace.h"#include "commands/async.h"#include "commands/tablecmds.h"#include "commands/trigger.h"#include "commands/user.h"#include "executor/spi.h"#include "libpq/be-fsstubs.h"#include "miscadmin.h"#include "storage/proc.h"#include "storage/sinval.h"#include "storage/smgr.h"#include "utils/guc.h"#include "utils/inval.h"#include "utils/memutils.h"#include "utils/portal.h"#include "utils/catcache.h"#include "utils/relcache.h"#include "pgstat.h"static void AbortTransaction(void);static void AtAbort_Cache(void);static void AtAbort_Locks(void);static void AtAbort_Memory(void);static void AtCleanup_Memory(void);static void AtCommit_Cache(void);static void AtCommit_LocalCache(void);static void AtCommit_Locks(void);static void AtCommit_Memory(void);static void AtStart_Cache(void);static void AtStart_Locks(void);static void AtStart_Memory(void);static void CallEOXactCallbacks(bool isCommit);static void CleanupTransaction(void);static void CommitTransaction(void);static void RecordTransactionAbort(void);static void StartTransaction(void);/* * global variables holding the current transaction state. */static TransactionStateData CurrentTransactionStateData = { 0, /* transaction id */ FirstCommandId, /* command id */ 0, /* scan command id */ 0x0, /* start time */ TRANS_DEFAULT, /* transaction state */ TBLOCK_DEFAULT /* transaction block state from the client * perspective */};static TransactionState CurrentTransactionState = &CurrentTransactionStateData;/* * User-tweakable parameters */int DefaultXactIsoLevel = XACT_READ_COMMITTED;int XactIsoLevel;bool DefaultXactReadOnly = false;bool XactReadOnly;int CommitDelay = 0; /* precommit delay in microseconds */int CommitSiblings = 5; /* number of concurrent xacts needed to * sleep *//* * List of add-on end-of-xact callbacks */typedef struct EOXactCallbackItem{ struct EOXactCallbackItem *next; EOXactCallback callback; void *arg;} EOXactCallbackItem;static EOXactCallbackItem *EOXact_callbacks = NULL;static void (*_RollbackFunc) (void *) = NULL;static void *_RollbackData = NULL;/* ---------------------------------------------------------------- * transaction state accessors * ---------------------------------------------------------------- */#ifdef NOT_USED/* -------------------------------- * TransactionFlushEnabled() * SetTransactionFlushEnabled() * * These are used to test and set the "TransactionFlushState" * variable. If this variable is true (the default), then * the system will flush all dirty buffers to disk at the end * of each transaction. If false then we are assuming the * buffer pool resides in stable main memory, in which case we * only do writes as necessary. * -------------------------------- */static int TransactionFlushState = 1;intTransactionFlushEnabled(void){ return TransactionFlushState;}voidSetTransactionFlushEnabled(bool state){ TransactionFlushState = (state == true);}#endif/* * IsTransactionState * * This returns true if we are currently running a query * within an executing transaction. */boolIsTransactionState(void){ TransactionState s = CurrentTransactionState; switch (s->state) { case TRANS_DEFAULT: return false; case TRANS_START: return true; case TRANS_INPROGRESS: return true; case TRANS_COMMIT: return true; case TRANS_ABORT: return true; } /* * Shouldn't get here, but lint is not happy with this... */ return false;}/* * 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) return true; return false;}/* * GetCurrentTransactionId */TransactionIdGetCurrentTransactionId(void){ TransactionState s = CurrentTransactionState; return s->transactionIdData;}/* * GetCurrentCommandId */CommandIdGetCurrentCommandId(void){ TransactionState s = CurrentTransactionState; return s->commandId;}/* * GetCurrentTransactionStartTime */AbsoluteTimeGetCurrentTransactionStartTime(void){ TransactionState s = CurrentTransactionState; return s->startTime;}/* * GetCurrentTransactionStartTimeUsec */AbsoluteTimeGetCurrentTransactionStartTimeUsec(int *msec){ TransactionState s = CurrentTransactionState; *msec = s->startTimeUsec; return s->startTime;}/* * TransactionIdIsCurrentTransactionId * * During bootstrap, we cheat and say "it's not my transaction ID" even though * it is. Along with transam.c's cheat to say that the bootstrap XID is * already committed, this causes the tqual.c routines to see previously * inserted tuples as committed, which is what we need during bootstrap. */boolTransactionIdIsCurrentTransactionId(TransactionId xid){ TransactionState s = CurrentTransactionState; if (AMI_OVERRIDE) { Assert(xid == BootstrapTransactionId); return false; } return TransactionIdEquals(xid, s->transactionIdData);}/* * CommandIdIsCurrentCommandId */boolCommandIdIsCurrentCommandId(CommandId cid){ TransactionState s = CurrentTransactionState; return (cid == s->commandId) ? true : false;}/* * CommandCounterIncrement */voidCommandCounterIncrement(void){ TransactionState s = CurrentTransactionState; s->commandId += 1; if (s->commandId == FirstCommandId) /* check for overflow */ ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("cannot have more than 2^32-1 commands in a transaction"))); /* Propagate new command ID into query snapshots, if set */ if (QuerySnapshot) QuerySnapshot->curcid = s->commandId; if (SerializableSnapshot) SerializableSnapshot->curcid = s->commandId; /* * make cache changes visible to me. AtCommit_LocalCache() instead of * AtCommit_Cache() is called here. */ AtCommit_LocalCache(); AtStart_Cache();}/* ---------------------------------------------------------------- * StartTransaction stuff * ---------------------------------------------------------------- *//* * AtStart_Cache */static voidAtStart_Cache(void){ AcceptInvalidationMessages();}/* * AtStart_Locks */static voidAtStart_Locks(void){ /* * at present, it is unknown to me what belongs here -cim 3/18/90 * * There isn't anything to do at the start of a xact for locks. -mer * 5/24/92 */}/* * AtStart_Memory */static voidAtStart_Memory(void){ /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -