📄 xact.c
字号:
(errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), /* translator: %s represents an SQL statement name */ errmsg("%s cannot run inside a transaction block", stmtType))); /* * Are we inside a function call? If the statement's parameter block * was allocated in QueryContext, assume it is an interactive command. * Otherwise assume it is coming from a function. */ if (!MemoryContextContains(QueryContext, stmtNode)) ereport(ERROR, (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), /* translator: %s represents an SQL statement name */ errmsg("%s cannot be executed from a function", stmtType))); /* If we got past IsTransactionBlock test, should be in default state */ if (CurrentTransactionState->blockState != TBLOCK_DEFAULT) elog(ERROR, "cannot prevent transaction chain"); /* all okay */}/* * RequireTransactionChain * * This routine is to be called by statements that must run inside * a transaction block, because they have no effects that persist past * transaction end (and so calling them outside a transaction block * is presumably an error). DECLARE CURSOR is an example. * * If we appear to be running inside a user-defined function, we do not * issue an error, since the function could issue more commands that make * use of the current statement's results. Thus this is an inverse for * PreventTransactionChain. * * 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. */voidRequireTransactionChain(void *stmtNode, const char *stmtType){ /* * xact block already started? */ if (IsTransactionBlock()) return; /* * Are we inside a function call? If the statement's parameter block * was allocated in QueryContext, assume it is an interactive command. * Otherwise assume it is coming from a function. */ if (!MemoryContextContains(QueryContext, stmtNode)) return; ereport(ERROR, (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), /* translator: %s represents an SQL statement name */ errmsg("%s may only be used in transaction blocks", stmtType)));}/* * Register or deregister callback functions for end-of-xact cleanup * * These functions are intended for use by dynamically loaded modules. * For built-in modules we generally just hardwire the appropriate calls * (mainly because it's easier to control the order that way, where needed). * * Note that the callback occurs post-commit or post-abort, so the callback * functions can only do noncritical cleanup. */voidRegisterEOXactCallback(EOXactCallback callback, void *arg){ EOXactCallbackItem *item; item = (EOXactCallbackItem *) MemoryContextAlloc(TopMemoryContext, sizeof(EOXactCallbackItem)); item->callback = callback; item->arg = arg; item->next = EOXact_callbacks; EOXact_callbacks = item;}voidUnregisterEOXactCallback(EOXactCallback callback, void *arg){ EOXactCallbackItem *item; EOXactCallbackItem *prev; prev = NULL; for (item = EOXact_callbacks; item; prev = item, item = item->next) { if (item->callback == callback && item->arg == arg) { if (prev) prev->next = item->next; else EOXact_callbacks = item->next; pfree(item); break; } }}static voidCallEOXactCallbacks(bool isCommit){ EOXactCallbackItem *item; for (item = EOXact_callbacks; item; item = item->next) { (*item->callback) (isCommit, item->arg); }}/* ---------------------------------------------------------------- * transaction block support * ---------------------------------------------------------------- *//* * BeginTransactionBlock */voidBeginTransactionBlock(void){ TransactionState s = CurrentTransactionState; /* * check the current transaction state */ if (s->blockState != TBLOCK_DEFAULT) ereport(WARNING, (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), errmsg("there is already a transaction in progress"))); /* * set the current transaction block state information appropriately * during begin processing */ s->blockState = TBLOCK_BEGIN; /* * do begin processing here. Nothing to do at present. */ /* * done with begin processing, set block state to inprogress */ s->blockState = TBLOCK_INPROGRESS;}/* * EndTransactionBlock */voidEndTransactionBlock(void){ TransactionState s = CurrentTransactionState; /* * check the current transaction state */ if (s->blockState == TBLOCK_INPROGRESS) { /* * here we are in a transaction block which should commit when we * get to the upcoming CommitTransactionCommand() so we set the * state to "END". CommitTransactionCommand() will recognize this * and commit the transaction and return us to the default state */ s->blockState = TBLOCK_END; return; } if (s->blockState == TBLOCK_ABORT) { /* * here, we are in a transaction block which aborted and since the * AbortTransaction() was already done, we do whatever is needed * and change to the special "END ABORT" state. The upcoming * CommitTransactionCommand() will recognise this and then put us * back in the default state. */ s->blockState = TBLOCK_ENDABORT; return; } /* * here, the user issued COMMIT when not inside a transaction. Issue a * WARNING and go to abort state. The upcoming call to * CommitTransactionCommand() will then put us back into the default * state. */ ereport(WARNING, (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), errmsg("there is no transaction in progress"))); AbortTransaction(); s->blockState = TBLOCK_ENDABORT;}/* * AbortTransactionBlock */#ifdef NOT_USEDstatic voidAbortTransactionBlock(void){ TransactionState s = CurrentTransactionState; /* * check the current transaction state */ if (s->blockState == TBLOCK_INPROGRESS) { /* * here we were inside a transaction block something screwed up * inside the system so we enter the abort state, do the abort * processing and then return. We remain in the abort state until * we see an END TRANSACTION command. */ s->blockState = TBLOCK_ABORT; AbortTransaction(); return; } /* * here, the user issued ABORT when not inside a transaction. Issue a * WARNING and go to abort state. The upcoming call to * CommitTransactionCommand() will then put us back into the default * state. */ ereport(WARNING, (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), errmsg("there is no transaction in progress"))); AbortTransaction(); s->blockState = TBLOCK_ENDABORT;}#endif/* * UserAbortTransactionBlock */voidUserAbortTransactionBlock(void){ TransactionState s = CurrentTransactionState; /* * if the transaction has already been automatically aborted with an * error, and the user subsequently types 'abort', allow it. (the * behavior is the same as if they had typed 'end'.) */ if (s->blockState == TBLOCK_ABORT) { s->blockState = TBLOCK_ENDABORT; return; } if (s->blockState == TBLOCK_INPROGRESS) { /* * here we were inside a transaction block and we got an abort * command from the user, so we move to the abort state, do the * abort processing and then change to the ENDABORT state so we * will end up in the default state after the upcoming * CommitTransactionCommand(). */ s->blockState = TBLOCK_ABORT; AbortTransaction(); s->blockState = TBLOCK_ENDABORT; return; } /* * here, the user issued ABORT when not inside a transaction. Issue a * WARNING and go to abort state. The upcoming call to * CommitTransactionCommand() will then put us back into the default * state. */ ereport(WARNING, (errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION), errmsg("there is no transaction in progress"))); AbortTransaction(); s->blockState = TBLOCK_ENDABORT;}/* * AbortOutOfAnyTransaction * * This routine is provided for error recovery purposes. It aborts any * active transaction or transaction block, leaving the system in a known * idle state. */voidAbortOutOfAnyTransaction(void){ TransactionState s = CurrentTransactionState; /* * Get out of any low-level transaction */ switch (s->state) { case TRANS_START: case TRANS_INPROGRESS: case TRANS_COMMIT: /* In a transaction, so clean up */ AbortTransaction(); CleanupTransaction(); break; case TRANS_ABORT: /* AbortTransaction already done, still need Cleanup */ CleanupTransaction(); break; case TRANS_DEFAULT: /* Not in a transaction, do nothing */ break; } /* * Now reset the high-level state */ s->blockState = TBLOCK_DEFAULT;}/* * IsTransactionBlock --- are we within a transaction block? */boolIsTransactionBlock(void){ TransactionState s = CurrentTransactionState; if (s->blockState == TBLOCK_DEFAULT) return false; return true;}/* * IsTransactionOrTransactionBlock --- are we within either a transaction * or a transaction block? (The backend is only really "idle" when this * returns false.) * * This should match up with IsTransactionBlock and IsTransactionState. */boolIsTransactionOrTransactionBlock(void){ TransactionState s = CurrentTransactionState; if (s->blockState == TBLOCK_DEFAULT && s->state == TRANS_DEFAULT) return false; return true;}/* * TransactionBlockStatusCode - return status code to send in ReadyForQuery */charTransactionBlockStatusCode(void){ TransactionState s = CurrentTransactionState; switch (s->blockState) { case TBLOCK_DEFAULT: return 'I'; /* idle --- not in transaction */ case TBLOCK_BEGIN: case TBLOCK_INPROGRESS: case TBLOCK_END: return 'T'; /* in transaction */ case TBLOCK_ABORT: case TBLOCK_ENDABORT: return 'E'; /* in failed transaction */ } /* should never get here */ elog(ERROR, "invalid transaction block state: %d", (int) s->blockState); return 0; /* keep compiler quiet */}/* * XLOG support routines */voidxact_redo(XLogRecPtr lsn, XLogRecord *record){ uint8 info = record->xl_info & ~XLR_INFO_MASK; if (info == XLOG_XACT_COMMIT) { TransactionIdCommit(record->xl_xid); /* SHOULD REMOVE FILES OF ALL DROPPED RELATIONS */ } else if (info == XLOG_XACT_ABORT) { TransactionIdAbort(record->xl_xid); /* SHOULD REMOVE FILES OF ALL FAILED-TO-BE-CREATED RELATIONS */ } else elog(PANIC, "xact_redo: unknown op code %u", info);}voidxact_undo(XLogRecPtr lsn, XLogRecord *record){ uint8 info = record->xl_info & ~XLR_INFO_MASK; if (info == XLOG_XACT_COMMIT) /* shouldn't be called by XLOG */ elog(PANIC, "xact_undo: can't undo committed xaction"); else if (info != XLOG_XACT_ABORT) elog(PANIC, "xact_redo: unknown op code %u", info);}voidxact_desc(char *buf, uint8 xl_info, char *rec){ uint8 info = xl_info & ~XLR_INFO_MASK; if (info == XLOG_XACT_COMMIT) { xl_xact_commit *xlrec = (xl_xact_commit *) rec; struct tm *tm = localtime(&xlrec->xtime); sprintf(buf + strlen(buf), "commit: %04u-%02u-%02u %02u:%02u:%02u", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); } else if (info == XLOG_XACT_ABORT) { xl_xact_abort *xlrec = (xl_xact_abort *) rec; struct tm *tm = localtime(&xlrec->xtime); sprintf(buf + strlen(buf), "abort: %04u-%02u-%02u %02u:%02u:%02u", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); } else strcat(buf, "UNKNOWN");}void XactPushRollback(void (*func) (void *), void *data){#ifdef XLOG_II if (_RollbackFunc != NULL) elog(PANIC, "XactPushRollback: already installed");#endif _RollbackFunc = func; _RollbackData = data;}voidXactPopRollback(void){ _RollbackFunc = NULL;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -