📄 xact.c
字号:
* We shouldn't have a transaction context already. */ Assert(TopTransactionContext == NULL); /* * Create a toplevel context for the transaction, and make it active. */ TopTransactionContext = AllocSetContextCreate(TopMemoryContext, "TopTransactionContext", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); MemoryContextSwitchTo(TopTransactionContext);}/* ---------------------------------------------------------------- * CommitTransaction stuff * ---------------------------------------------------------------- *//* * RecordTransactionCommit */voidRecordTransactionCommit(void){ /* * If we made neither any XLOG entries nor any temp-rel updates, we * can omit recording the transaction commit at all. */ if (MyXactMadeXLogEntry || MyXactMadeTempRelUpdate) { TransactionId xid = GetCurrentTransactionId(); bool madeTCentries; XLogRecPtr recptr; /* Tell bufmgr and smgr to prepare for commit */ BufmgrCommit(); START_CRIT_SECTION(); /* * If our transaction made any transaction-controlled XLOG entries, * we need to lock out checkpoint start between writing our XLOG * record and updating pg_clog. Otherwise it is possible for the * checkpoint to set REDO after the XLOG record but fail to flush the * pg_clog update to disk, leading to loss of the transaction commit * if we crash a little later. Slightly klugy fix for problem * discovered 2004-08-10. * * (If it made no transaction-controlled XLOG entries, its XID * appears nowhere in permanent storage, so no one else will ever care * if it committed; so it doesn't matter if we lose the commit flag.) * * Note we only need a shared lock. */ madeTCentries = (MyLastRecPtr.xrecoff != 0); if (madeTCentries) LWLockAcquire(CheckpointStartLock, LW_SHARED); /* * We only need to log the commit in XLOG if the transaction made * any transaction-controlled XLOG entries. */ if (madeTCentries) { /* Need to emit a commit record */ XLogRecData rdata; xl_xact_commit xlrec; xlrec.xtime = time(NULL); rdata.buffer = InvalidBuffer; rdata.data = (char *) (&xlrec); rdata.len = SizeOfXactCommit; rdata.next = NULL; /* * XXX SHOULD SAVE ARRAY OF RELFILENODE-s TO DROP */ recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_COMMIT, &rdata); } else { /* Just flush through last record written by me */ recptr = ProcLastRecEnd; } /* * We must flush our XLOG entries to disk if we made any XLOG * entries, whether in or out of transaction control. For * example, if we reported a nextval() result to the client, this * ensures that any XLOG record generated by nextval will hit the * disk before we report the transaction committed. */ if (MyXactMadeXLogEntry) { /* * Sleep before flush! So we can flush more than one commit * records per single fsync. (The idea is some other backend * may do the XLogFlush while we're sleeping. This needs work * still, because on most Unixen, the minimum select() delay * is 10msec or more, which is way too long.) * * We do not sleep if enableFsync is not turned on, nor if there * are fewer than CommitSiblings other backends with active * transactions. */ if (CommitDelay > 0 && enableFsync && CountActiveBackends() >= CommitSiblings) { struct timeval delay; delay.tv_sec = 0; delay.tv_usec = CommitDelay; (void) select(0, NULL, NULL, NULL, &delay); } XLogFlush(recptr); } /* * We must mark the transaction committed in clog if its XID * appears either in permanent rels or in local temporary rels. We * test this by seeing if we made transaction-controlled entries * *OR* local-rel tuple updates. Note that if we made only the * latter, we have not emitted an XLOG record for our commit, and * so in the event of a crash the clog update might be lost. This * is okay because no one else will ever care whether we * committed. */ if (MyLastRecPtr.xrecoff != 0 || MyXactMadeTempRelUpdate) TransactionIdCommit(xid); /* Unlock checkpoint lock if we acquired it */ if (madeTCentries) LWLockRelease(CheckpointStartLock); END_CRIT_SECTION(); } /* Break the chain of back-links in the XLOG records I output */ MyLastRecPtr.xrecoff = 0; MyXactMadeXLogEntry = false; MyXactMadeTempRelUpdate = false; /* Show myself as out of the transaction in PGPROC array */ MyProc->logRec.xrecoff = 0;}/* * AtCommit_Cache */static voidAtCommit_Cache(void){ /* * Clean up the relation cache. */ AtEOXact_RelationCache(true); /* * Make catalog changes visible to all backends. */ AtEOXactInvalidationMessages(true);}/* * AtCommit_LocalCache */static voidAtCommit_LocalCache(void){ /* * Make catalog changes visible to me for the next command. */ CommandEndInvalidationMessages(true);}/* * AtCommit_Locks */static voidAtCommit_Locks(void){ /* * XXX What if ProcReleaseLocks fails? (race condition?) * * Then you're up a creek! -mer 5/24/92 */ ProcReleaseLocks(true);}/* * AtCommit_Memory */static voidAtCommit_Memory(void){ /* * Now that we're "out" of a transaction, have the system allocate * things in the top memory context instead of per-transaction * contexts. */ MemoryContextSwitchTo(TopMemoryContext); /* * Release all transaction-local memory. */ Assert(TopTransactionContext != NULL); MemoryContextDelete(TopTransactionContext); TopTransactionContext = NULL;}/* ---------------------------------------------------------------- * AbortTransaction stuff * ---------------------------------------------------------------- *//* * RecordTransactionAbort */static voidRecordTransactionAbort(void){ /* * If we made neither any transaction-controlled XLOG entries nor any * temp-rel updates, we can omit recording the transaction abort at * all. No one will ever care that it aborted. */ if (MyLastRecPtr.xrecoff != 0 || MyXactMadeTempRelUpdate) { TransactionId xid = GetCurrentTransactionId(); /* * Catch the scenario where we aborted partway through * RecordTransactionCommit ... */ if (TransactionIdDidCommit(xid)) elog(PANIC, "cannot abort transaction %u, it was already committed", xid); START_CRIT_SECTION(); /* * We only need to log the abort in XLOG if the transaction made * any transaction-controlled XLOG entries. (Otherwise, its XID * appears nowhere in permanent storage, so no one else will ever * care if it committed.) We do not flush XLOG to disk in any * case, since the default assumption after a crash would be that * we aborted, anyway. * For the same reason, we don't need to worry about interlocking * against checkpoint start. */ if (MyLastRecPtr.xrecoff != 0) { XLogRecData rdata; xl_xact_abort xlrec; XLogRecPtr recptr; xlrec.xtime = time(NULL); rdata.buffer = InvalidBuffer; rdata.data = (char *) (&xlrec); rdata.len = SizeOfXactAbort; rdata.next = NULL; /* * SHOULD SAVE ARRAY OF RELFILENODE-s TO DROP */ recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT, &rdata); } /* * Mark the transaction aborted in clog. This is not absolutely * necessary but we may as well do it while we are here. */ TransactionIdAbort(xid); END_CRIT_SECTION(); } /* Break the chain of back-links in the XLOG records I output */ MyLastRecPtr.xrecoff = 0; MyXactMadeXLogEntry = false; MyXactMadeTempRelUpdate = false; /* Show myself as out of the transaction in PGPROC array */ MyProc->logRec.xrecoff = 0;}/* * AtAbort_Cache */static voidAtAbort_Cache(void){ AtEOXact_RelationCache(false); AtEOXactInvalidationMessages(false);}/* * AtAbort_Locks */static voidAtAbort_Locks(void){ /* * XXX What if ProcReleaseLocks() fails? (race condition?) * * Then you're up a creek without a paddle! -mer */ ProcReleaseLocks(false);}/* * AtAbort_Memory */static voidAtAbort_Memory(void){ /* * Make sure we are in a valid context (not a child of * TopTransactionContext...). Note that it is possible for this code * to be called when we aren't in a transaction at all; go directly to * TopMemoryContext in that case. */ if (TopTransactionContext != NULL) { MemoryContextSwitchTo(TopTransactionContext); /* * We do not want to destroy the transaction's global state yet, * so we can't free any memory here. */ } else MemoryContextSwitchTo(TopMemoryContext);}/* ---------------------------------------------------------------- * CleanupTransaction stuff * ---------------------------------------------------------------- *//* * AtCleanup_Memory */static voidAtCleanup_Memory(void){ /* * Now that we're "out" of a transaction, have the system allocate * things in the top memory context instead of per-transaction * contexts. */ MemoryContextSwitchTo(TopMemoryContext); /* * Release all transaction-local memory. */ if (TopTransactionContext != NULL) MemoryContextDelete(TopTransactionContext); TopTransactionContext = NULL;}/* ---------------------------------------------------------------- * interface routines * ---------------------------------------------------------------- *//* * StartTransaction */static voidStartTransaction(void){ TransactionState s = CurrentTransactionState; /* * check the current transaction state */ if (s->state != TRANS_DEFAULT) elog(WARNING, "StartTransaction and not in default state"); /* * set the current transaction state information appropriately during * start processing */ s->state = TRANS_START; /* * Make sure we've freed any old snapshot, and reset xact state variables */ FreeXactSnapshot(); XactIsoLevel = DefaultXactIsoLevel; XactReadOnly = DefaultXactReadOnly; /* * generate a new transaction id */ s->transactionIdData = GetNewTransactionId(); XactLockTableInsert(s->transactionIdData); /* * initialize current transaction state fields */ s->commandId = FirstCommandId; s->startTime = GetCurrentAbsoluteTimeUsec(&(s->startTimeUsec)); /* * initialize the various transaction subsystems */ AtStart_Memory(); AtStart_Cache(); AtStart_Locks(); /* * Tell the trigger manager to we're starting a transaction */ DeferredTriggerBeginXact(); /* * done with start processing, set current transaction state to "in * progress" */ s->state = TRANS_INPROGRESS;}/* * CommitTransaction */static voidCommitTransaction(void){ TransactionState s = CurrentTransactionState; /* * check the current transaction state */ if (s->state != TRANS_INPROGRESS) elog(WARNING, "CommitTransaction and not in in-progress state"); /* * Tell the trigger manager that this transaction is about to be * committed. He'll invoke all trigger deferred until XACT before we * really start on committing the transaction. */ DeferredTriggerEndXact(); /* * Similarly, let ON COMMIT management do its thing before we start to * commit. */ PreCommit_on_commit_actions(); /* Prevent cancel/die interrupt while cleaning up */ HOLD_INTERRUPTS(); /* * set the current transaction state information appropriately during * the abort processing */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -