📄 xact.c
字号:
s->state = TRANS_COMMIT; /* * Do pre-commit processing (most of this stuff requires database * access, and in fact could still cause an error...) */ AtCommit_Portals(); /* handle commit for large objects [ PA, 7/17/98 ] */ /* XXX probably this does not belong here */ lo_commit(true); /* NOTIFY commit must come before lower-level cleanup */ AtCommit_Notify(); /* Update the flat password file if we changed pg_shadow or pg_group */ AtEOXact_UpdatePasswordFile(true); /* * Here is where we really truly commit. */ RecordTransactionCommit(); /* * Let others know about no transaction in progress by me. Note that * this must be done _before_ releasing locks we hold and _after_ * RecordTransactionCommit. * * LWLockAcquire(SInvalLock) is required: UPDATE with xid 0 is blocked by * xid 1' UPDATE, xid 1 is doing commit while xid 2 gets snapshot - if * xid 2' GetSnapshotData sees xid 1 as running then it must see xid 0 * as running as well or it will see two tuple versions - one deleted * by xid 1 and one inserted by xid 0. See notes in GetSnapshotData. */ if (MyProc != (PGPROC *) NULL) { /* Lock SInvalLock because that's what GetSnapshotData uses. */ LWLockAcquire(SInvalLock, LW_EXCLUSIVE); MyProc->xid = InvalidTransactionId; MyProc->xmin = InvalidTransactionId; LWLockRelease(SInvalLock); } /* * This is all post-commit cleanup. Note that if an error is raised * here, it's too late to abort the transaction. This should be just * noncritical resource releasing. * * The ordering of operations is not entirely random. The idea is: * release resources visible to other backends (eg, files, buffer * pins); then release locks; then release backend-local resources. We * want to release locks at the point where any backend waiting for us * will see our transaction as being fully cleaned up. */ smgrDoPendingDeletes(true); AtCommit_Cache(); AtEOXact_Buffers(true); /* smgrcommit already done */ AtCommit_Locks(); CallEOXactCallbacks(true); AtEOXact_GUC(true); AtEOXact_SPI(); AtEOXact_gist(); AtEOXact_hash(); AtEOXact_nbtree(); AtEOXact_rtree(); AtEOXact_on_commit_actions(true); AtEOXact_Namespace(true); AtEOXact_CatCache(true); AtEOXact_Files(); pgstat_count_xact_commit(); AtCommit_Memory(); /* * done with commit processing, set current transaction state back to * default */ s->state = TRANS_DEFAULT; RESUME_INTERRUPTS();}/* * AbortTransaction */static voidAbortTransaction(void){ TransactionState s = CurrentTransactionState; /* Prevent cancel/die interrupt while cleaning up */ HOLD_INTERRUPTS(); /* * Release any LW locks we might be holding as quickly as possible. * (Regular locks, however, must be held till we finish aborting.) * Releasing LW locks is critical since we might try to grab them * again while cleaning up! */ LWLockReleaseAll(); /* Clean up buffer I/O and buffer context locks, too */ AbortBufferIO(); UnlockBuffers(); /* * Also clean up any open wait for lock, since the lock manager will * choke if we try to wait for another lock before doing this. */ LockWaitCancel(); /* * check the current transaction state */ if (s->state != TRANS_INPROGRESS) elog(WARNING, "AbortTransaction and not in in-progress state"); /* * set the current transaction state information appropriately during * the abort processing */ s->state = TRANS_ABORT; /* Make sure we are in a valid memory context */ AtAbort_Memory(); /* * Reset user id which might have been changed transiently */ SetUserId(GetSessionUserId()); /* * do abort processing */ DeferredTriggerAbortXact(); AtAbort_Portals(); lo_commit(false); /* 'false' means it's abort */ AtAbort_Notify(); AtEOXact_UpdatePasswordFile(false); /* Advertise the fact that we aborted in pg_clog. */ RecordTransactionAbort(); /* * Let others know about no transaction in progress by me. Note that * this must be done _before_ releasing locks we hold and _after_ * RecordTransactionAbort. */ if (MyProc != (PGPROC *) NULL) { /* Lock SInvalLock because that's what GetSnapshotData uses. */ LWLockAcquire(SInvalLock, LW_EXCLUSIVE); MyProc->xid = InvalidTransactionId; MyProc->xmin = InvalidTransactionId; LWLockRelease(SInvalLock); } /* * Post-abort cleanup. See notes in CommitTransaction() concerning * ordering. */ smgrDoPendingDeletes(false); AtAbort_Cache(); AtEOXact_Buffers(false); smgrabort(); AtAbort_Locks(); CallEOXactCallbacks(false); AtEOXact_GUC(false); AtEOXact_SPI(); AtEOXact_gist(); AtEOXact_hash(); AtEOXact_nbtree(); AtEOXact_rtree(); AtEOXact_on_commit_actions(false); AtEOXact_Namespace(false); AtEOXact_CatCache(false); AtEOXact_Files(); SetReindexProcessing(InvalidOid, InvalidOid); pgstat_count_xact_rollback(); /* * State remains TRANS_ABORT until CleanupTransaction(). */ RESUME_INTERRUPTS();}/* * CleanupTransaction */static voidCleanupTransaction(void){ TransactionState s = CurrentTransactionState; /* * State should still be TRANS_ABORT from AbortTransaction(). */ if (s->state != TRANS_ABORT) elog(FATAL, "CleanupTransaction and not in abort state"); /* * do abort cleanup processing */ AtCleanup_Portals(); /* now safe to release portal memory */ AtCleanup_Memory(); /* and transaction memory */ /* * done with abort processing, set current transaction state back to * default */ s->state = TRANS_DEFAULT;}/* * StartTransactionCommand */voidStartTransactionCommand(void){ TransactionState s = CurrentTransactionState; switch (s->blockState) { /* * if we aren't in a transaction block, we just do our usual * start transaction. */ case TBLOCK_DEFAULT: StartTransaction(); break; /* * We should never experience this -- if we do it means the * BEGIN state was not changed in the previous * CommitTransactionCommand(). If we get it, we print a * warning and change to the in-progress state. */ case TBLOCK_BEGIN: elog(WARNING, "StartTransactionCommand: unexpected TBLOCK_BEGIN"); s->blockState = TBLOCK_INPROGRESS; break; /* * This is the case when are somewhere in a transaction block * and about to start a new command. For now we do nothing * but someday we may do command-local resource * initialization. */ case TBLOCK_INPROGRESS: break; /* * As with BEGIN, we should never experience this if we do it * means the END state was not changed in the previous * CommitTransactionCommand(). If we get it, we print a * warning, commit the transaction, start a new transaction * and change to the default state. */ case TBLOCK_END: elog(WARNING, "StartTransactionCommand: unexpected TBLOCK_END"); s->blockState = TBLOCK_DEFAULT; CommitTransaction(); StartTransaction(); break; /* * Here we are in the middle of a transaction block but one of * the commands caused an abort so we do nothing but remain in * the abort state. Eventually we will get to the "END * TRANSACTION" which will set things straight. */ case TBLOCK_ABORT: break; /* * This means we somehow aborted and the last call to * CommitTransactionCommand() didn't clear the state so we * remain in the ENDABORT state and maybe next time we get to * CommitTransactionCommand() the state will get reset to * default. */ case TBLOCK_ENDABORT: elog(WARNING, "StartTransactionCommand: unexpected TBLOCK_ENDABORT"); break; } /* * We must switch to TopTransactionContext before returning. This is * already done if we called StartTransaction, otherwise not. */ Assert(TopTransactionContext != NULL); MemoryContextSwitchTo(TopTransactionContext);}/* * CommitTransactionCommand */voidCommitTransactionCommand(void){ TransactionState s = CurrentTransactionState; switch (s->blockState) { /* * If we aren't in a transaction block, just do our usual * transaction commit. */ case TBLOCK_DEFAULT: CommitTransaction(); break; /* * This is the case right after we get a "BEGIN TRANSACTION" * command, but the user hasn't done anything else yet, so we * change to the "transaction block in progress" state and * return. */ case TBLOCK_BEGIN: s->blockState = TBLOCK_INPROGRESS; break; /* * This is the case when we have finished executing a command * someplace within a transaction block. We increment the * command counter and return. */ case TBLOCK_INPROGRESS: CommandCounterIncrement(); break; /* * This is the case when we just got the "END TRANSACTION" * statement, so we commit the transaction and go back to the * default state. */ case TBLOCK_END: CommitTransaction(); s->blockState = TBLOCK_DEFAULT; break; /* * Here we are in the middle of a transaction block but one of * the commands caused an abort so we do nothing but remain in * the abort state. Eventually we will get to the "END * TRANSACTION" which will set things straight. */ case TBLOCK_ABORT: break; /* * Here we were in an aborted transaction block which just * processed the "END TRANSACTION" command from the user, so * clean up and return to the default state. */ case TBLOCK_ENDABORT: CleanupTransaction(); s->blockState = TBLOCK_DEFAULT; break; }}/* * AbortCurrentTransaction */voidAbortCurrentTransaction(void){ TransactionState s = CurrentTransactionState; switch (s->blockState) { /* * if we aren't in a transaction block, we just do the basic * abort & cleanup transaction. */ case TBLOCK_DEFAULT: AbortTransaction(); CleanupTransaction(); break; /* * If we are in the TBLOCK_BEGIN it means something screwed up * right after reading "BEGIN TRANSACTION" so we enter the * abort state. Eventually an "END TRANSACTION" will fix * things. */ case TBLOCK_BEGIN: s->blockState = TBLOCK_ABORT; AbortTransaction(); /* CleanupTransaction happens when we exit TBLOCK_ABORT */ break; /* * This is the case when are somewhere in a transaction block * which aborted so we abort the transaction and set the ABORT * state. Eventually an "END TRANSACTION" will fix things and * restore us to a normal state. */ case TBLOCK_INPROGRESS: s->blockState = TBLOCK_ABORT; AbortTransaction(); /* CleanupTransaction happens when we exit TBLOCK_ABORT */ break; /* * Here, the system was fouled up just after the user wanted * to end the transaction block so we abort the transaction * and put us back into the default state. */ case TBLOCK_END: s->blockState = TBLOCK_DEFAULT; AbortTransaction(); CleanupTransaction(); break; /* * Here, we are already in an aborted transaction state and * are waiting for an "END TRANSACTION" to come along and lo * and behold, we abort again! So we just remain in the abort * state. */ case TBLOCK_ABORT: break; /* * Here we were in an aborted transaction block which just * processed the "END TRANSACTION" command but somehow aborted * again.. since we must have done the abort processing, we * clean up and return to the default state. */ case TBLOCK_ENDABORT: CleanupTransaction(); s->blockState = TBLOCK_DEFAULT; break; }}/* * PreventTransactionChain * * This routine is to be called by statements that must not run inside * a transaction block, typically because they have non-rollback-able * side effects or do internal commits. * * If we have already started a transaction block, issue an error; also issue * an error if we appear to be running inside a user-defined function (which * could issue more commands and possibly cause a failure after the statement * completes). * * stmtNode: pointer to parameter block for statement; this is used in * a very klugy way to determine whether we are inside a function. * stmtType: statement type name for error messages. */voidPreventTransactionChain(void *stmtNode, const char *stmtType){ /* * xact block already started? */ if (IsTransactionBlock()) ereport(ERROR,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -