PostgreSQL中函數(shù)AssignTransactionId的實(shí)現(xiàn)邏輯是什么

本篇內(nèi)容介紹了“PostgreSQL中函數(shù)AssignTransactionId的實(shí)現(xiàn)邏輯是什么”的有關(guān)知識(shí),在實(shí)際案例的操作過程中,不少人都會(huì)遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

成都創(chuàng)新互聯(lián)長期為數(shù)千家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺(tái),與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為忻城企業(yè)提供專業(yè)的做網(wǎng)站、網(wǎng)站設(shè)計(jì),忻城網(wǎng)站改版等技術(shù)服務(wù)。擁有十多年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。

一、數(shù)據(jù)結(jié)構(gòu)

靜態(tài)變量
當(dāng)前事務(wù)狀態(tài)CurrentTransactionState

/*
 * CurrentTransactionState always points to the current transaction state
 * block.  It will point to TopTransactionStateData when not in a
 * transaction at all, or when in a top-level transaction.
 * CurrentTransactionState通常指向當(dāng)前事務(wù)塊.
 * 如不處于事務(wù)中或者處于頂層事務(wù)中,則指向TopTransactionStateData
 */
static TransactionStateData TopTransactionStateData = {
    .state = TRANS_DEFAULT,
    .blockState = TBLOCK_DEFAULT,
};
/*
 * unreportedXids holds XIDs of all subtransactions that have not yet been
 * reported in an XLOG_XACT_ASSIGNMENT record.
 * unreportedXids保存所有尚未在XLOG_XACT_ASSIGNMENT記錄的子事務(wù).
 */
static int  nUnreportedXids;
static TransactionId unreportedXids[PGPROC_MAX_CACHED_SUBXIDS];
static TransactionState CurrentTransactionState = &TopTransactionStateData;
/*
 * The subtransaction ID and command ID assignment counters are global
 * to a whole transaction, so we do not keep them in the state stack.
 * subtransaction ID和command ID全局計(jì)數(shù)器,對(duì)事務(wù)可見,在state棧中不記錄這些信息.
 */
static SubTransactionId currentSubTransactionId;
static CommandId currentCommandId;
static bool currentCommandIdUsed;

TransactionState
事務(wù)狀態(tài)結(jié)構(gòu)體

/*
 *  transaction states - transaction state from server perspective
 *  事務(wù)狀態(tài)枚舉 - 服務(wù)器視角的事務(wù)狀態(tài)
 */
typedef enum TransState
{
    TRANS_DEFAULT,              /* idle 空閑 */
    TRANS_START,                /* transaction starting 事務(wù)啟動(dòng) */
    TRANS_INPROGRESS,           /* inside a valid transaction 進(jìn)行中 */
    TRANS_COMMIT,               /* commit in progress 提交中 */
    TRANS_ABORT,                /* abort in progress 回滾中 */
    TRANS_PREPARE               /* prepare in progress 準(zhǔn)備中 */
} TransState;
/*
 *  transaction block states - transaction state of client queries
 *  事務(wù)塊狀態(tài) - 客戶端查詢的事務(wù)狀態(tài)
 *
 * Note: the subtransaction states are used only for non-topmost
 * transactions; the others appear only in the topmost transaction.
 * 注意:subtransaction只用于非頂層事務(wù);其他字段用于頂層事務(wù).
 */
typedef enum TBlockState
{
    /* not-in-transaction-block states 未進(jìn)入事務(wù)塊狀態(tài) */
    TBLOCK_DEFAULT,             /* idle 空閑  */
    TBLOCK_STARTED,             /* running single-query transaction 單個(gè)查詢事務(wù) */
    /* transaction block states 事務(wù)塊狀態(tài) */
    TBLOCK_BEGIN,               /* starting transaction block 開始事務(wù)塊 */
    TBLOCK_INPROGRESS,          /* live transaction 進(jìn)行中 */
    TBLOCK_IMPLICIT_INPROGRESS, /* live transaction after implicit BEGIN 隱式事務(wù),進(jìn)行中 */
    TBLOCK_PARALLEL_INPROGRESS, /* live transaction inside parallel worker 并行worker中的事務(wù),進(jìn)行中 */
    TBLOCK_END,                 /* COMMIT received 接收到COMMIT */
    TBLOCK_ABORT,               /* failed xact, awaiting ROLLBACK 失敗,等待ROLLBACK */
    TBLOCK_ABORT_END,           /* failed xact, ROLLBACK received 失敗,已接收ROLLBACK */
    TBLOCK_ABORT_PENDING,       /* live xact, ROLLBACK received 進(jìn)行中,接收到ROLLBACK */
    TBLOCK_PREPARE,             /* live xact, PREPARE received 進(jìn)行中,接收到PREPARE */
    /* subtransaction states 子事務(wù)狀態(tài) */
    TBLOCK_SUBBEGIN,            /* starting a subtransaction 開啟 */
    TBLOCK_SUBINPROGRESS,       /* live subtransaction 進(jìn)行中 */
    TBLOCK_SUBRELEASE,          /* RELEASE received 接收到RELEASE */
    TBLOCK_SUBCOMMIT,           /* COMMIT received while TBLOCK_SUBINPROGRESS 進(jìn)行中,接收到COMMIT */
    TBLOCK_SUBABORT,            /* failed subxact, awaiting ROLLBACK 失敗,等待ROLLBACK */
    TBLOCK_SUBABORT_END,        /* failed subxact, ROLLBACK received 失敗,已接收ROLLBACK */
    TBLOCK_SUBABORT_PENDING,    /* live subxact, ROLLBACK received 進(jìn)行中,接收到ROLLBACK */
    TBLOCK_SUBRESTART,          /* live subxact, ROLLBACK TO received 進(jìn)行中,接收到ROLLBACK TO */
    TBLOCK_SUBABORT_RESTART     /* failed subxact, ROLLBACK TO received 失敗,已接收ROLLBACK TO */
} TBlockState;
/*
 *  transaction state structure
 *  事務(wù)狀態(tài)結(jié)構(gòu)體
 */
typedef struct TransactionStateData
{
    //事務(wù)ID
    TransactionId transactionId;    /* my XID, or Invalid if none */
    //子事務(wù)ID
    SubTransactionId subTransactionId;  /* my subxact ID */
    //保存點(diǎn)名稱
    char       *name;           /* savepoint name, if any */
    //保存點(diǎn)級(jí)別
    int         savepointLevel; /* savepoint level */
    //低級(jí)別的事務(wù)狀態(tài)
    TransState  state;          /* low-level state */
    //高級(jí)別的事務(wù)狀態(tài)
    TBlockState blockState;     /* high-level state */
    //事務(wù)嵌套深度
    int         nestingLevel;   /* transaction nesting depth */
    //GUC上下文嵌套深度
    int         gucNestLevel;   /* GUC context nesting depth */
    //事務(wù)生命周期上下文
    MemoryContext curTransactionContext;    /* my xact-lifetime context */
    //查詢資源
    ResourceOwner curTransactionOwner;  /* my query resources */
    //按XID順序保存的已提交的子事務(wù)ID
    TransactionId *childXids;   /* subcommitted child XIDs, in XID order */
    //childXids數(shù)組大小
    int         nChildXids;     /* # of subcommitted child XIDs */
    //分配的childXids數(shù)組空間
    int         maxChildXids;   /* allocated size of childXids[] */
    //上一個(gè)CurrentUserId
    Oid         prevUser;       /* previous CurrentUserId setting */
    //上一個(gè)SecurityRestrictionContext
    int         prevSecContext; /* previous SecurityRestrictionContext */
    //上一事務(wù)是否只讀?
    bool        prevXactReadOnly;   /* entry-time xact r/o state */
    //是否處于Recovery?
    bool        startedInRecovery;  /* did we start in recovery? */
    //XID是否已保存在WAL Record中?
    bool        didLogXid;      /* has xid been included in WAL record? */
    //Enter/ExitParallelMode計(jì)數(shù)器
    int         parallelModeLevel;  /* Enter/ExitParallelMode counter */
    //父事務(wù)狀態(tài)
    struct TransactionStateData *parent;    /* back link to parent */
} TransactionStateData;
//結(jié)構(gòu)體指針
typedef TransactionStateData *TransactionState;

二、源碼解讀

AssignTransactionId函數(shù),給定的TransactionState分配一個(gè)新的持久化事務(wù)號(hào)XID,在此函數(shù)調(diào)用前,不會(huì)為事務(wù)分配XIDs.

/*
 * AssignTransactionId
 *
 * Assigns a new permanent XID to the given TransactionState.
 * We do not assign XIDs to transactions until/unless this is called.
 * Also, any parent TransactionStates that don't yet have XIDs are assigned
 * one; this maintains the invariant that a child transaction has an XID
 * following its parent's.
 * 為給定的TransactionState分配一個(gè)新的持久化事務(wù)號(hào)XID.
 * 在此函數(shù)調(diào)用前,我們不會(huì)為事務(wù)分配XIDs.
 * 同時(shí),所有尚未獲得XIDs的父TransactionStates也會(huì)分配事務(wù)號(hào),
 *   這可以確保子事務(wù)的事務(wù)號(hào)在父事務(wù)之后.
 */
static void
AssignTransactionId(TransactionState s)
{
    bool        isSubXact = (s->parent != NULL);
    ResourceOwner currentOwner;
    bool        log_unknown_top = false;
    /* Assert that caller didn't screw up */
    //確保調(diào)用者沒有搞砸
    Assert(!TransactionIdIsValid(s->transactionId));
    Assert(s->state == TRANS_INPROGRESS);
    /*
     * Workers synchronize transaction state at the beginning of each parallel
     * operation, so we can't account for new XIDs at this point.
     * 在每個(gè)并行操作前,Parallel Workers同步事務(wù)狀態(tài),
     *   因此我們不能在這時(shí)候請求XIDs
     */
    if (IsInParallelMode() || IsParallelWorker())
        elog(ERROR, "cannot assign XIDs during a parallel operation");
    /*
     * Ensure parent(s) have XIDs, so that a child always has an XID later
     * than its parent.  Mustn't recurse here, or we might get a stack overflow
     * if we're at the bottom of a huge stack of subtransactions none of which
     * have XIDs yet.
     * 確保父事務(wù)已分配XIDs,這樣可以確保子事務(wù)的XID后于父事務(wù).
     * 不能在這里遞歸執(zhí)行,否則如果我們在一個(gè)未分配XID子事務(wù)大棧的底部,可能會(huì)遇到棧溢出
     */
    if (isSubXact && !TransactionIdIsValid(s->parent->transactionId))
    {
        TransactionState p = s->parent;
        TransactionState *parents;
        size_t      parentOffset = 0;
        parents = palloc(sizeof(TransactionState) * s->nestingLevel);
        while (p != NULL && !TransactionIdIsValid(p->transactionId))
        {
            parents[parentOffset++] = p;
            p = p->parent;
        }
        /*
         * This is technically a recursive call, but the recursion will never
         * be more than one layer deep.
         * 遞歸調(diào)用,但遞歸不會(huì)超過一層
         */
        while (parentOffset != 0)
            AssignTransactionId(parents[--parentOffset]);
        pfree(parents);
    }
    /*
     * When wal_level=logical, guarantee that a subtransaction's xid can only
     * be seen in the WAL stream if its toplevel xid has been logged before.
     * If necessary we log an xact_assignment record with fewer than
     * PGPROC_MAX_CACHED_SUBXIDS. Note that it is fine if didLogXid isn't set
     * for a transaction even though it appears in a WAL record, we just might
     * superfluously log something. That can happen when an xid is included
     * somewhere inside a wal record, but not in XLogRecord->xl_xid, like in
     * xl_standby_locks.
     * 如wal_level=logical,確保子事務(wù)XID在頂層XID已被"日志"的情況只可以被WAL stream看到.
     * 如果有必要,我們將使用小于PGPROC_MAX_CACHED_SUBXIDS的值來記錄xact_assignment記錄.
     * 請注意,即使didLogXid出現(xiàn)在WAL記錄中,如果它沒有為事務(wù)設(shè)置,也沒有問題,
     *   我們可能只是多余地記錄了一些東西。
     * 當(dāng)一個(gè)xid出現(xiàn)在WAL Record中的某個(gè)地方,但不在XLogRecord->xl_xid中
     *   (比如在xl_standby_locks中)時(shí),就會(huì)發(fā)生這種情況。
     */
    if (isSubXact && XLogLogicalInfoActive() &&
        !TopTransactionStateData.didLogXid)
        log_unknown_top = true;
    /*
     * Generate a new Xid and record it in PG_PROC and pg_subtrans.
     * 生成一個(gè)新的XID,并記錄在PG_PROC和pg_subtrans中
     *
     * NB: we must make the subtrans entry BEFORE the Xid appears anywhere in
     * shared storage other than PG_PROC; because if there's no room for it in
     * PG_PROC, the subtrans entry is needed to ensure that other backends see
     * the Xid as "running".  See GetNewTransactionId.
     * 注意:我們必須在Xid出現(xiàn)在除PG_PROC之外的共享存儲(chǔ)之前構(gòu)造subtrans條目.
     * 因?yàn)槿绻赑G_PROC沒有空閑空間,子事務(wù)條目需要確保其他進(jìn)程看到該XID正在運(yùn)行.
     * 參考函數(shù)GetNewTransactionId說明.
     * 
     */
    s->transactionId = GetNewTransactionId(isSubXact);
    if (!isSubXact)
        XactTopTransactionId = s->transactionId;
    if (isSubXact)
        SubTransSetParent(s->transactionId, s->parent->transactionId);
    /*
     * If it's a top-level transaction, the predicate locking system needs to
     * be told about it too.
     * 如為頂層事務(wù),謂詞鎖系統(tǒng)也需要了解此事務(wù).
     */
    if (!isSubXact)
        RegisterPredicateLockingXid(s->transactionId);
    /*
     * Acquire lock on the transaction XID.  (We assume this cannot block.) We
     * have to ensure that the lock is assigned to the transaction's own
     * ResourceOwner.
     * 請求鎖(我們假定這樣做不好引起阻塞).我們必須確保鎖已被分配給事務(wù)自己的ResourceOwner.
     */
    currentOwner = CurrentResourceOwner;
    CurrentResourceOwner = s->curTransactionOwner;
    XactLockTableInsert(s->transactionId);
    CurrentResourceOwner = currentOwner;
    /*
     * Every PGPROC_MAX_CACHED_SUBXIDS assigned transaction ids within each
     * top-level transaction we issue a WAL record for the assignment. We
     * include the top-level xid and all the subxids that have not yet been
     * reported using XLOG_XACT_ASSIGNMENT records.
     * 在每個(gè)頂級(jí)事務(wù)中分配的每個(gè)PGPROC_MAX_CACHED_SUBXIDS事務(wù)id,
     *   我們都會(huì)為分配記錄一條WAL記錄。
     * 該記錄包括頂級(jí)的xid和所有尚未使用XLOG_XACT_ASSIGNMENT記錄報(bào)告的子xid。
     *
     * This is required to limit the amount of shared memory required in a hot
     * standby server to keep track of in-progress XIDs. See notes for
     * RecordKnownAssignedTransactionIds().
     * 在跟蹤進(jìn)行中的XIDs的備機(jī)上,需要控制共享內(nèi)存的大小.
     * 參見RecordKnownAssignedTransactionIds()函數(shù)說明.
     *
     * We don't keep track of the immediate parent of each subxid, only the
     * top-level transaction that each subxact belongs to. This is correct in
     * recovery only because aborted subtransactions are separately WAL
     * logged.
     * 我們不需要跟蹤父事務(wù)的每個(gè)子事務(wù),只需要跟蹤子事務(wù)歸屬的頂層事務(wù)即可.
     * 這樣可行是因?yàn)樵诨謴?fù)中,已回滾的子事務(wù)是通過WAL單獨(dú)記錄的.
     *
     * This is correct even for the case where several levels above us didn't
     * have an xid assigned as we recursed up to them beforehand.
     * 即使在我們之前遞歸到上面的幾個(gè)級(jí)別時(shí)沒有分配xid的情況下,這也是正確的。
     */
    if (isSubXact && XLogStandbyInfoActive())
    {
        unreportedXids[nUnreportedXids] = s->transactionId;
        nUnreportedXids++;
        /*
         * ensure this test matches similar one in
         * RecoverPreparedTransactions()
         * 確保在RecoverPreparedTransactions()中可以匹配到相似的.
         */
        if (nUnreportedXids >= PGPROC_MAX_CACHED_SUBXIDS ||
            log_unknown_top)
        {
            xl_xact_assignment xlrec;
            /*
             * xtop is always set by now because we recurse up transaction
             * stack to the highest unassigned xid and then come back down
             * xtop現(xiàn)在已經(jīng)設(shè)置好了,因?yàn)槲覀儗⑹聞?wù)堆棧遞歸到最高的未分配的xid,然后再返回
             */
            xlrec.xtop = GetTopTransactionId();
            Assert(TransactionIdIsValid(xlrec.xtop));
            xlrec.nsubxacts = nUnreportedXids;
            XLogBeginInsert();
            XLogRegisterData((char *) &xlrec, MinSizeOfXactAssignment);
            XLogRegisterData((char *) unreportedXids,
                             nUnreportedXids * sizeof(TransactionId));
            (void) XLogInsert(RM_XACT_ID, XLOG_XACT_ASSIGNMENT);
            nUnreportedXids = 0;
            /* mark top, not current xact as having been logged */
            //標(biāo)記為最頂層,而不是當(dāng)前已記錄日志的xact
            TopTransactionStateData.didLogXid = true;
        }
    }
}

三、跟蹤分析

執(zhí)行txid_current,觸發(fā)函數(shù)調(diào)用

11:10:36 (xdb@[local]:5432)testdb=# begin;
BEGIN
11:40:20 (xdb@[local]:5432)testdb=#* select txid_current_if_assigned();
 txid_current_if_assigned 
--------------------------
(1 row)
11:40:43 (xdb@[local]:5432)testdb=#* select txid_current();

啟動(dòng)gdb,設(shè)置斷點(diǎn)

(gdb) b AssignTransactionId
Breakpoint 5 at 0x546a4c: file xact.c, line 491.
(gdb) c
Continuing.
Breakpoint 5, AssignTransactionId (s=0xf9c720 <TopTransactionStateData>) at xact.c:491
491     bool        isSubXact = (s->parent != NULL);
(gdb)

查看調(diào)用棧

(gdb) bt
#0  AssignTransactionId (s=0xf9c720 <TopTransactionStateData>) at xact.c:491
#1  0x000000000054693d in GetTopTransactionId () at xact.c:392
#2  0x00000000009fe1f3 in txid_current (fcinfo=0x25835a0) at txid.c:443
#3  0x00000000006cfebd in ExecInterpExpr (state=0x25834b8, econtext=0x25831a8, isnull=0x7ffe3d4a31f7)
    at execExprInterp.c:654
#4  0x00000000006d1ac6 in ExecInterpExprStillValid (state=0x25834b8, econtext=0x25831a8, isNull=0x7ffe3d4a31f7)
    at execExprInterp.c:1786
#5  0x00000000007140dd in ExecEvalExprSwitchContext (state=0x25834b8, econtext=0x25831a8, isNull=0x7ffe3d4a31f7)
    at ../../../src/include/executor/executor.h:303
#6  0x000000000071414b in ExecProject (projInfo=0x25834b0) at ../../../src/include/executor/executor.h:337
#7  0x0000000000714323 in ExecResult (pstate=0x2583090) at nodeResult.c:136
#8  0x00000000006e4c30 in ExecProcNodeFirst (node=0x2583090) at execProcnode.c:445
#9  0x00000000006d9974 in ExecProcNode (node=0x2583090) at ../../../src/include/executor/executor.h:237
#10 0x00000000006dc22d in ExecutePlan (estate=0x2582e78, planstate=0x2583090, use_parallel_mode=false, 
    operation=CMD_SELECT, sendTuples=true, numberTuples=0, direction=ForwardScanDirection, dest=0x24cd0a0, 
    execute_once=true) at execMain.c:1723
#11 0x00000000006d9f5c in standard_ExecutorRun (queryDesc=0x256b0c8, direction=ForwardScanDirection, count=0, 
    execute_once=true) at execMain.c:364
#12 0x00000000006d9d7f in ExecutorRun (queryDesc=0x256b0c8, direction=ForwardScanDirection, count=0, execute_once=true)
    at execMain.c:307
#13 0x00000000008ccf5a in PortalRunSelect (portal=0x250c748, forward=true, count=0, dest=0x24cd0a0) at pquery.c:932
#14 0x00000000008ccbf3 in PortalRun (portal=0x250c748, count=9223372036854775807, isTopLevel=true, run_once=true, 
    dest=0x24cd0a0, altdest=0x24cd0a0, completionTag=0x7ffe3d4a3570 "") at pquery.c:773
#15 0x00000000008c6b1e in exec_simple_query (query_string=0x24a6ec8 "select txid_current();") at postgres.c:1145
#16 0x00000000008cae70 in PostgresMain (argc=1, argv=0x24d2dc8, dbname=0x24d2c30 "testdb", username=0x24a3ba8 "xdb")
    at postgres.c:4182
    #17 0x000000000082642b in BackendRun (port=0x24c8c00) at postmaster.c:4361
---Type <return> to continue, or q <return> to quit---
#18 0x0000000000825b8f in BackendStartup (port=0x24c8c00) at postmaster.c:4033
#19 0x0000000000821f1c in ServerLoop () at postmaster.c:1706
#20 0x00000000008217b4 in PostmasterMain (argc=1, argv=0x24a1b60) at postmaster.c:1379
#21 0x00000000007488ef in main (argc=1, argv=0x24a1b60) at main.c:228

輸入?yún)?shù)TransactionState(全局變量,指向TopTransactionStateData)

(gdb) p s
$13 = (TransactionState) 0xf9c720 <TopTransactionStateData>
(gdb) p *s
$14 = {transactionId = 0, subTransactionId = 1, name = 0x0, savepointLevel = 0, state = TRANS_INPROGRESS, 
  blockState = TBLOCK_INPROGRESS, nestingLevel = 1, gucNestLevel = 1, curTransactionContext = 0x2523850, 
  curTransactionOwner = 0x24d4868, childXids = 0x0, nChildXids = 0, maxChildXids = 0, prevUser = 10, prevSecContext = 0, 
  prevXactReadOnly = false, startedInRecovery = false, didLogXid = false, parallelModeLevel = 0, parent = 0x0}
(gdb)

初始化部分變量并驗(yàn)證

(gdb) n
493     bool        log_unknown_top = false;
(gdb) 
496     Assert(!TransactionIdIsValid(s->transactionId));
(gdb) 
497     Assert(s->state == TRANS_INPROGRESS);
(gdb)

獲取事務(wù)號(hào)

(gdb) 
503     if (IsInParallelMode() || IsParallelWorker())
(gdb) 
512     if (isSubXact && !TransactionIdIsValid(s->parent->transactionId))
(gdb) 
545     if (isSubXact && XLogLogicalInfoActive() &&
(gdb) 
557     s->transactionId = GetNewTransactionId(isSubXact);
(gdb) 
558     if (!isSubXact)
(gdb) p s->transactionId
$15 = 2407
(gdb)

注冊,并設(shè)置其他信息

(gdb) n
559         XactTopTransactionId = s->transactionId;
(gdb) 
561     if (isSubXact)
(gdb) 
568     if (!isSubXact)
(gdb) 
569         RegisterPredicateLockingXid(s->transactionId);
(gdb) 
576     currentOwner = CurrentResourceOwner;
(gdb) 
577     CurrentResourceOwner = s->curTransactionOwner;
(gdb) 
579     XactLockTableInsert(s->transactionId);
(gdb) 
581     CurrentResourceOwner = currentOwner;
(gdb) 
601     if (isSubXact && XLogStandbyInfoActive())
(gdb) 
635 }

完成調(diào)用

(gdb) 
GetTopTransactionId () at xact.c:393
393     return XactTopTransactionId;
(gdb)

“PostgreSQL中函數(shù)AssignTransactionId的實(shí)現(xiàn)邏輯是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

文章標(biāo)題:PostgreSQL中函數(shù)AssignTransactionId的實(shí)現(xiàn)邏輯是什么
網(wǎng)頁網(wǎng)址:http://muchs.cn/article28/pjjecp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供做網(wǎng)站、靜態(tài)網(wǎng)站、搜索引擎優(yōu)化、、網(wǎng)站維護(hù)、自適應(yīng)網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

成都app開發(fā)公司