/* * FinishPreparedTransaction: execute COMMIT PREPARED or ROLLBACK PREPARED */ void FinishPreparedTransaction(const char *gid, bool isCommit) { GlobalTransaction gxact; TransactionId xid; char *buf; char *bufptr; TwoPhaseFileHeader *hdr; TransactionId *children; RelFileNode *commitrels; RelFileNode *abortrels; int i; /* * Validate the GID, and lock the GXACT to ensure that two backends do not * try to commit the same GID at once. */ gxact = LockGXact(gid, GetUserId()); xid = gxact->proc.xid; /* * Read and validate the state file */ buf = ReadTwoPhaseFile(xid); if (buf == NULL) ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("two-phase state file for transaction %u is corrupt", xid))); /* * Disassemble the header area */ hdr = (TwoPhaseFileHeader *) buf; Assert(TransactionIdEquals(hdr->xid, xid)); bufptr = buf + MAXALIGN(sizeof(TwoPhaseFileHeader)); children = (TransactionId *) bufptr; bufptr += MAXALIGN(hdr->nsubxacts * sizeof(TransactionId)); commitrels = (RelFileNode *) bufptr; bufptr += MAXALIGN(hdr->ncommitrels * sizeof(RelFileNode)); abortrels = (RelFileNode *) bufptr; bufptr += MAXALIGN(hdr->nabortrels * sizeof(RelFileNode)); /* * The order of operations here is critical: make the XLOG entry for * commit or abort, then mark the transaction committed or aborted in * pg_clog, then remove its PGPROC from the global ProcArray (which means * TransactionIdIsInProgress will stop saying the prepared xact is in * progress), then run the post-commit or post-abort callbacks. The * callbacks will release the locks the transaction held. */ if (isCommit) RecordTransactionCommitPrepared(xid, hdr->nsubxacts, children, hdr->ncommitrels, commitrels); else RecordTransactionAbortPrepared(xid, hdr->nsubxacts, children, hdr->nabortrels, abortrels); ProcArrayRemove(&gxact->proc); /* * In case we fail while running the callbacks, mark the gxact invalid so * no one else will try to commit/rollback, and so it can be recycled * properly later. It is still locked by our XID so it won't go away yet. * * (We assume it's safe to do this without taking TwoPhaseStateLock.) */ gxact->valid = false; /* * We have to remove any files that were supposed to be dropped. For * consistency with the regular xact.c code paths, must do this before * releasing locks, so do it before running the callbacks. * * NB: this code knows that we couldn't be dropping any temp rels ... */ if (isCommit) { for (i = 0; i < hdr->ncommitrels; i++) smgrdounlink(smgropen(commitrels[i]), false, false); } else { for (i = 0; i < hdr->nabortrels; i++) smgrdounlink(smgropen(abortrels[i]), false, false); } /* And now do the callbacks */ if (isCommit) ProcessRecords(bufptr, xid, twophase_postcommit_callbacks); else ProcessRecords(bufptr, xid, twophase_postabort_callbacks); pgstat_count_xact_commit(); /* * And now we can clean up our mess. */ RemoveTwoPhaseFile(xid, true); RemoveGXact(gxact); pfree(buf); }
/* * CommitTransaction */ static void CommitTransaction(void) { TransactionState s = CurrentTransactionState; /* * check the current transaction state */ if (s->state != TRANS_INPROGRESS) elog(WARNING, "CommitTransaction and not in in-progress state"); /* * Do pre-commit processing (most of this stuff requires database * access, and in fact could still cause an error...) */ /* * 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(); /* Close open cursors */ AtCommit_Portals(); /* * Let ON COMMIT management do its thing (must happen after closing * cursors, to avoid dangling-reference problems) */ PreCommit_on_commit_actions(); /* 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); /* Prevent cancel/die interrupt while cleaning up */ HOLD_INTERRUPTS(); /* * set the current transaction state information appropriately during * the abort processing */ s->state = TRANS_COMMIT; /* * 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(); }