xact.c
来自「PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统」· C语言 代码 · 共 2,442 行 · 第 1/5 页
C
2,442 行
/* * 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 or will delete files. (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.) * * We do not flush XLOG to disk unless deleting files, 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 || nrels > 0) { XLogRecData rdata[3]; int lastrdata = 0; xl_xact_abort xlrec; XLogRecPtr recptr; xlrec.xtime = time(NULL); xlrec.nrels = nrels; xlrec.nsubxacts = nchildren; rdata[0].data = (char *) (&xlrec); rdata[0].len = MinSizeOfXactAbort; rdata[0].buffer = InvalidBuffer; /* dump rels to delete */ if (nrels > 0) { rdata[0].next = &(rdata[1]); rdata[1].data = (char *) rels; rdata[1].len = nrels * sizeof(RelFileNode); rdata[1].buffer = InvalidBuffer; lastrdata = 1; } /* dump committed child Xids */ if (nchildren > 0) { rdata[lastrdata].next = &(rdata[2]); rdata[2].data = (char *) children; rdata[2].len = nchildren * sizeof(TransactionId); rdata[2].buffer = InvalidBuffer; lastrdata = 2; } rdata[lastrdata].next = NULL; recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT, rdata); /* Must flush if we are deleting files... */ if (nrels > 0) XLogFlush(recptr); } /* * Mark the transaction aborted in clog. This is not absolutely * necessary but we may as well do it while we are here. * * The ordering here isn't critical but it seems best to mark the * parent first. This assures an atomic transition of all the * subtransactions to aborted state from the point of view of * concurrent TransactionIdDidAbort calls. */ TransactionIdAbort(xid); TransactionIdAbortTree(nchildren, children); END_CRIT_SECTION(); } /* Break the chain of back-links in the XLOG records I output */ MyLastRecPtr.xrecoff = 0; MyXactMadeXLogEntry = false; MyXactMadeTempRelUpdate = false; /* And clean up local data */ if (rels) pfree(rels); if (children) pfree(children);}/* * 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);}/* * AtSubAbort_Memory */static voidAtSubAbort_Memory(void){ Assert(TopTransactionContext != NULL); MemoryContextSwitchTo(TopTransactionContext);}/* * AtAbort_ResourceOwner */static voidAtAbort_ResourceOwner(void){ /* * Make sure we have a valid ResourceOwner, if possible (else it will be * NULL, which is OK) */ CurrentResourceOwner = TopTransactionResourceOwner;}/* * AtSubAbort_ResourceOwner */static voidAtSubAbort_ResourceOwner(void){ TransactionState s = CurrentTransactionState; /* Make sure we have a valid ResourceOwner */ CurrentResourceOwner = s->curTransactionOwner;}/* * AtSubAbort_childXids */static voidAtSubAbort_childXids(void){ TransactionState s = CurrentTransactionState; /* * We keep the child-XID lists in TopTransactionContext (see * AtSubCommit_childXids). This means we'd better free the list * explicitly at abort to avoid leakage. */ list_free(s->childXids); s->childXids = NIL;}/* * RecordSubTransactionAbort */static voidRecordSubTransactionAbort(void){ int nrels; RelFileNode *rels; TransactionId xid = GetCurrentTransactionId(); int nchildren; TransactionId *children; /* Get data needed for abort record */ nrels = smgrGetPendingDeletes(false, &rels); nchildren = xactGetCommittedChildren(&children); /* * If we made neither any transaction-controlled XLOG entries nor any * temp-rel updates, and are not going to delete any files, we can omit * recording the transaction abort at all. No one will ever care that it * aborted. (These tests cover our whole transaction tree, and therefore * may mark subxacts that don't really need it, but it's probably not * worth being tenser.) * * In this case we needn't worry about marking subcommitted children as * aborted, because they didn't mark themselves as subcommitted in the * first place; see the optimization in RecordSubTransactionCommit. */ if (MyLastRecPtr.xrecoff != 0 || MyXactMadeTempRelUpdate || nrels > 0) { START_CRIT_SECTION(); /* * We only need to log the abort in XLOG if the transaction made any * transaction-controlled XLOG entries or will delete files. */ if (MyLastRecPtr.xrecoff != 0 || nrels > 0) { XLogRecData rdata[3]; int lastrdata = 0; xl_xact_abort xlrec; XLogRecPtr recptr; xlrec.xtime = time(NULL); xlrec.nrels = nrels; xlrec.nsubxacts = nchildren; rdata[0].data = (char *) (&xlrec); rdata[0].len = MinSizeOfXactAbort; rdata[0].buffer = InvalidBuffer; /* dump rels to delete */ if (nrels > 0) { rdata[0].next = &(rdata[1]); rdata[1].data = (char *) rels; rdata[1].len = nrels * sizeof(RelFileNode); rdata[1].buffer = InvalidBuffer; lastrdata = 1; } /* dump committed child Xids */ if (nchildren > 0) { rdata[lastrdata].next = &(rdata[2]); rdata[2].data = (char *) children; rdata[2].len = nchildren * sizeof(TransactionId); rdata[2].buffer = InvalidBuffer; lastrdata = 2; } rdata[lastrdata].next = NULL; recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT, rdata); /* Must flush if we are deleting files... */ if (nrels > 0) XLogFlush(recptr); } /* * Mark the transaction aborted in clog. This is not absolutely * necessary but XactLockTableWait makes use of it to avoid waiting * for already-aborted subtransactions. */ TransactionIdAbort(xid); TransactionIdAbortTree(nchildren, children); END_CRIT_SECTION(); } /* * We can immediately remove failed XIDs from PGPROC's cache of running * child XIDs. It's easiest to do it here while we have the child XID * array at hand, even though in the main-transaction case the equivalent * work happens just after return from RecordTransactionAbort. */ XidCacheRemoveRunningXids(xid, nchildren, children); /* And clean up local data */ if (rels) pfree(rels); if (children) pfree(children);}/* ---------------------------------------------------------------- * 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); Assert(CurrentTransactionState->parent == NULL); /* * Release all transaction-local memory. */ if (TopTransactionContext != NULL) MemoryContextDelete(TopTransactionContext); TopTransactionContext = NULL; CurTransactionContext = NULL; CurrentTransactionState->curTransactionContext = NULL;}/* ---------------------------------------------------------------- * CleanupSubTransaction stuff * ---------------------------------------------------------------- *//* * AtSubCleanup_Memory */static voidAtSubCleanup_Memory(void){ TransactionState s = CurrentTransactionState; Assert(s->parent != NULL); /* Make sure we're not in an about-to-be-deleted context */ MemoryContextSwitchTo(s->parent->curTransactionContext); CurTransactionContext = s->parent->curTransactionContext; /* * Delete the subxact local memory contexts. Its CurTransactionContext can * go too (note this also kills CurTransactionContexts from any children * of the subxact). */ if (s->curTransactionContext) MemoryContextDelete(s->curTransactionContext); s->curTransactionContext = NULL;}/* ---------------------------------------------------------------- * interface routines * ---------------------------------------------------------------- *//* * StartTransaction */static voidStartTransaction(void){ TransactionState s; /* * Let's just make sure the state stack is empty */ s = &TopTransactionStateData; CurrentTransactionState = s; /* * check the current transaction state */ if (s->state != TRANS_DEFAULT) elog(WARNING, "StartTransaction while in %s state", TransStateAsString(s->state)); /* * set the current transaction state information appropriately during * start processing */ s->state = TRANS_START; s->transactionId = InvalidTransactionId; /* until assigned */ /* * Make sure we've freed any old snapshot, and reset xact state variables */ FreeXactSnapshot(); XactIsoLevel = DefaultXactIsoLevel; XactReadOnly = DefaultXactReadOnly; /* * reinitialize within-transaction counters */ s->subTransactionId = TopSubTransactionId; currentSubTransactionId = TopSubTransactionId; currentCommandId = FirstCommandId; /* * must initialize resource-management stuff first */ AtStart_Memory(); AtStart_ResourceOwner(); /* * generate a new transaction id */ s->transactionId = GetNewTransactionId(false); XactLockTableInsert(s->transactionId); /* * set now() */ xactStartTimestamp = GetCurrentTimestamp(); /* * initialize current transaction state fields */ s->nestingLevel = 1; s->childXids = NIL; /* * You might expect to see "s->currentUser = GetUserId();" here, but you * won't because it doesn't work during startup; the userid isn't set yet * during a backend's first transaction start. We only use the * currentUser field in sub-transaction state structs. * * prevXactReadOnly is also valid only in sub-transactions. */ /* * initialize other subsystems for new transaction */ AtStart_Inval(); AtStart_Cache(); AfterTriggerBeginXact(); /* * done with start processing, set current transaction state to "in * progress" */ s->state = TRANS_INPROGRESS; ShowTransactionState("StartTransaction");}/* * CommitTransaction * * NB: if you change this routine, better look at PrepareTransaction too! */static voidCommitTransaction(void){ TransactionState s = CurrentTransactionState; ShowTransactionState("CommitTransaction"); /* * check the current transaction state */ if (s->state != TRANS_INPROGRESS) elog(WARNING, "CommitTransaction while in %s state", TransStateAsString(s->state)); Assert(s->parent == NULL); /* * Do pre-commit processing (most of this stuff requires database access, * and in fact could still cause an error...) * * It is possible for CommitHoldablePortals to invoke functions that queue * deferred triggers, and it's also possible that triggers create holdable * cursors. So we have to loop until there's nothing left to do. */ for (;;) { /* * Fire all currently pending deferred triggers. */ AfterTriggerFireDeferred(); /* * Convert any open holdable cursors into static portals. If there * weren't any, we are done ... otherwise loop back to check if they * queued deferred triggers. Lather, rinse, repeat. */ if (!CommitHoldablePortals()) break; } /* Now we can shut down the deferred-trigger manager */ AfterTriggerEndXact(true); /* Close any open regular cursors */ AtCommit_Portals(); /* * Let ON COMMIT management do its thing (must happen after closing * cursors, to avoid dangling-reference problems) */ PreCommit_on_commit_actions(); /* close large objects before lower-level cleanup */ AtEOXact_LargeObject(true); /* NOTIFY commit must come before lower-level cleanup */ AtCommit_Notify();
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?