• PostgreSQL的学习心得和知识总结(一百四十五)|深入理解PostgreSQL数据库之ShowTransactionState的使用及父子事务有限状态机



    注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下:

    1、参考书籍:《PostgreSQL数据库内核分析》
    2、参考书籍:《数据库事务处理的艺术:事务管理与并发控制》
    3、PostgreSQL数据库仓库链接,点击前往
    4、日本著名PostgreSQL数据库专家 铃木启修 网站主页,点击前往
    5、参考书籍:《PostgreSQL中文手册》
    6、参考书籍:《PostgreSQL指南:内幕探索》,点击前往


    1、本文内容全部来源于开源社区 GitHub和以上博主的贡献,本文也免费开源(可能会存在问题,评论区等待大佬们的指正)
    2、本文目的:开源共享 抛砖引玉 一起学习
    3、本文不提供任何资源 不存在任何交易 与任何组织和机构无关
    4、大家可以根据需要自行 复制粘贴以及作为其他个人用途,但是不允许转载 不允许商用 (写作不易,还请见谅 💖)
    5、本文内容基于PostgreSQL master源码开发而成




    文章快速说明索引

    学习目标:

    做数据库内核开发久了就会有一种 少年得志,年少轻狂 的错觉,然鹅细细一品觉得自己其实不算特别优秀 远远没有达到自己想要的。也许光鲜的表面掩盖了空洞的内在,每每想到于此,皆有夜半临渊如履薄冰之感。为了睡上几个踏实觉,即日起 暂缓其他基于PostgreSQL数据库的兼容功能开发,近段时间 将着重于学习分享Postgres的基础知识和实践内幕。


    学习内容:(详见目录)

    1、深入理解PostgreSQL数据库之ShowTransactionState的使用及父子事务有限状态机


    学习时间:

    2024年06月02日 21:22:11


    学习产出:

    1、PostgreSQL数据库基础知识回顾 1个
    2、CSDN 技术博客 1篇
    3、PostgreSQL数据库内核深入学习


    注:下面我们所有的学习环境是Centos8+PostgreSQL master+Oracle19C+MySQL8.0

    postgres=# select version();
                                                      version                                                   
    ------------------------------------------------------------------------------------------------------------
     PostgreSQL 17devel on x86_64-pc-linux-gnu, compiled by gcc (GCC) 8.5.0 20210514 (Red Hat 8.5.0-21), 64-bit
    (1 row)
    
    postgres=#
    
    #-----------------------------------------------------------------------------#
    
    SQL> select * from v$version;          
    
    BANNER        Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production	
    BANNER_FULL	  Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production Version 19.17.0.0.0	
    BANNER_LEGACY Oracle Database 19c EE Extreme Perf Release 19.0.0.0.0 - Production	
    CON_ID 0
    
    
    #-----------------------------------------------------------------------------#
    
    mysql> select version();
    +-----------+
    | version() |
    +-----------+
    | 8.0.27    |
    +-----------+
    1 row in set (0.06 sec)
    
    mysql>
    

    重点数据结构说明

    // src/backend/access/transam/xact.c
    
    /*
     *	transaction states - transaction state from server perspective
     *	事务状态 - 从服务器角度的事务状态
     */
    typedef enum TransState
    {
    	TRANS_DEFAULT,				/* idle */
    	TRANS_START,				/* transaction starting */
    	TRANS_INPROGRESS,			/* inside a valid transaction */
    	TRANS_COMMIT,				/* commit in progress */
    	TRANS_ABORT,				/* abort in progress */
    	TRANS_PREPARE,				/* prepare in progress */
    } TransState;
    
    /*
     *	transaction block states - transaction state of client queries
     *	事务块状态 - 客户端查询的事务状态
     *
     * Note: the subtransaction states are used only for non-topmost
     * transactions; the others appear only in the topmost transaction.
     * 注意:子事务(subtransaction)状态仅用于非最顶层事务;其他状态仅出现在最顶层事务中。
     */
    typedef enum TBlockState
    {
    	/* not-in-transaction-block states */
    	TBLOCK_DEFAULT,				/* idle */
    	TBLOCK_STARTED,				/* running single-query transaction */
    
    	/* transaction block states */
    	TBLOCK_BEGIN,				/* starting transaction block */
    	TBLOCK_INPROGRESS,			/* live transaction */
    	TBLOCK_IMPLICIT_INPROGRESS, /* live transaction after implicit BEGIN */
    	TBLOCK_PARALLEL_INPROGRESS, /* live transaction inside parallel worker */
    	TBLOCK_END,					/* COMMIT received */
    	TBLOCK_ABORT,				/* failed xact, awaiting ROLLBACK */
    	TBLOCK_ABORT_END,			/* failed xact, ROLLBACK received */
    	TBLOCK_ABORT_PENDING,		/* live xact, ROLLBACK received */
    	TBLOCK_PREPARE,				/* live xact, PREPARE received */
    
    	/* subtransaction states */
    	TBLOCK_SUBBEGIN,			/* starting a subtransaction */
    	TBLOCK_SUBINPROGRESS,		/* live subtransaction */
    	TBLOCK_SUBRELEASE,			/* RELEASE received */
    	TBLOCK_SUBCOMMIT,			/* COMMIT received while TBLOCK_SUBINPROGRESS */
    	TBLOCK_SUBABORT,			/* failed subxact, awaiting ROLLBACK */
    	TBLOCK_SUBABORT_END,		/* failed subxact, ROLLBACK received */
    	TBLOCK_SUBABORT_PENDING,	/* live subxact, ROLLBACK received */
    	TBLOCK_SUBRESTART,			/* live subxact, ROLLBACK TO received */
    	TBLOCK_SUBABORT_RESTART,	/* failed subxact, ROLLBACK TO received */
    } TBlockState;
    

    首先我们看一下今天的重点函数ShowTransactionState的内部实现,如下:

    /*
     * ShowTransactionState
     *		Debug support
     */
    static void
    ShowTransactionState(const char *str)
    {
    	/* skip work if message will definitely not be printed */
    	if (message_level_is_interesting(DEBUG5))
    		ShowTransactionStateRec(str, CurrentTransactionState);
    }
    
    /*
     * ShowTransactionStateRec
     *		Recursive subroutine for ShowTransactionState
     *		ShowTransactionState 的递归子程序
     */
    static void
    ShowTransactionStateRec(const char *str, TransactionState s)
    {
    	StringInfoData buf;
    
    	if (s->parent)
    	{
    		/*
    		 * Since this function recurses, it could be driven to stack overflow.
    		 * This is just a debugging aid, so we can leave out some details
    		 * instead of erroring out with check_stack_depth().
    		 *  
    		 * 由于此函数递归,可能会导致堆栈溢出
    		 * 这只是一个调试辅助工具,因此我们可以省略一些细节
    		 * 而不是使用 check_stack_depth() 出错
    		 */
    		if (stack_is_too_deep())
    			ereport(DEBUG5,
    					(errmsg_internal("%s(%d): parent omitted to avoid stack overflow",
    									 str, s->nestingLevel)));
    		else
    			ShowTransactionStateRec(str, s->parent);
    	}
    
    	initStringInfo(&buf);
    	if (s->nChildXids > 0)
    	{
    		int			i;
    
    		appendStringInfo(&buf, ", children: %u", s->childXids[0]);
    		for (i = 1; i < s->nChildXids; i++)
    			appendStringInfo(&buf, " %u", s->childXids[i]);
    	}
    	ereport(DEBUG5,
    			(errmsg_internal("%s(%d) name: %s; blockState: %s; state: %s, xid/subid/cid: %u/%u/%u%s%s",
    							 str, s->nestingLevel,
    							 PointerIsValid(s->name) ? s->name : "unnamed",
    							 BlockStateAsString(s->blockState),
    							 TransStateAsString(s->state),
    							 (unsigned int) XidFromFullTransactionId(s->fullTransactionId),
    							 (unsigned int) s->subTransactionId,
    							 (unsigned int) currentCommandId,
    							 currentCommandIdUsed ? " (used)" : "",
    							 buf.data)));
    	pfree(buf.data);
    }
    

    我们对上面逻辑进行修改,patch如下:

    [postgres@localhost:~/postgres → master]$ git diff src/backend/access/transam/xact.c
    diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c
    index 4f4ce75762..336b4d1ee7 100644
    --- a/src/backend/access/transam/xact.c
    +++ b/src/backend/access/transam/xact.c
    @@ -5599,7 +5599,7 @@ static void
     ShowTransactionState(const char *str)
     {
            /* skip work if message will definitely not be printed */
    -       if (message_level_is_interesting(DEBUG5))
    +       // if (message_level_is_interesting(DEBUG5))
                    ShowTransactionStateRec(str, CurrentTransactionState);
     }
     
    @@ -5620,7 +5620,7 @@ ShowTransactionStateRec(const char *str, TransactionState s)
                     * instead of erroring out with check_stack_depth().
                     */
                    if (stack_is_too_deep())
    -                       ereport(DEBUG5,
    +                       ereport(INFO,
                                            (errmsg_internal("%s(%d): parent omitted to avoid stack overflow",
                                                                             str, s->nestingLevel)));
                    else
    @@ -5636,7 +5636,7 @@ ShowTransactionStateRec(const char *str, TransactionState s)
                    for (i = 1; i < s->nChildXids; i++)
                            appendStringInfo(&buf, " %u", s->childXids[i]);
            }
    -       ereport(DEBUG5,
    +       ereport(INFO,
                            (errmsg_internal("%s(%d) name: %s; blockState: %s; state: %s, xid/subid/cid: %u/%u/%u%s%s",
                                                             str, s->nestingLevel,
                                                             PointerIsValid(s->name) ? s->name : "unnamed",
    [postgres@localhost:~/postgres → master]$
    

    源码解析案例分解

    案例展示,如下:

    -- INFO0
    [postgres@localhost:~/test/bin]$ ./psql 
    INFO:  CommitTransaction(1) name: unnamed; blockState: STARTED; state: INPROGRESS, xid/subid/cid: 0/1/0
    psql (17beta1)
    Type "help" for help.
    
    -- INFO1
    postgres=# create table t1 (id int);
    INFO:  StartTransaction(1) name: unnamed; blockState: DEFAULT; state: INPROGRESS, xid/subid/cid: 0/1/0
    INFO:  CommitTransaction(1) name: unnamed; blockState: STARTED; state: INPROGRESS, xid/subid/cid: 738/1/1
    CREATE TABLE
    -- INFO2
    postgres=# begin;
    INFO:  StartTransaction(1) name: unnamed; blockState: DEFAULT; state: INPROGRESS, xid/subid/cid: 0/1/0
    BEGIN
    postgres=*# insert into t1 values(1);
    INSERT 0 1
    -- INFO3
    postgres=*# savepoint sp1;
    INFO:  StartSubTransaction(1) name: unnamed; blockState: INPROGRESS; state: INPROGRESS, xid/subid/cid: 739/1/1
    INFO:  StartSubTransaction(2) name: sp1; blockState: SUBBEGIN; state: INPROGRESS, xid/subid/cid: 0/2/1
    SAVEPOINT
    postgres=*# insert into t1 values(2);
    INSERT 0 1
    -- INFO4
    postgres=*# savepoint sp2;
    INFO:  StartSubTransaction(1) name: unnamed; blockState: INPROGRESS; state: INPROGRESS, xid/subid/cid: 739/1/2
    INFO:  StartSubTransaction(2) name: sp1; blockState: SUBINPROGRESS; state: INPROGRESS, xid/subid/cid: 740/2/2
    INFO:  StartSubTransaction(3) name: sp2; blockState: SUBBEGIN; state: INPROGRESS, xid/subid/cid: 0/3/2
    SAVEPOINT
    postgres=*# insert into t1 values(3);
    INSERT 0 1
    -- INFO5
    postgres=*# savepoint sp3;
    INFO:  StartSubTransaction(1) name: unnamed; blockState: INPROGRESS; state: INPROGRESS, xid/subid/cid: 739/1/3
    INFO:  StartSubTransaction(2) name: sp1; blockState: SUBINPROGRESS; state: INPROGRESS, xid/subid/cid: 740/2/3
    INFO:  StartSubTransaction(3) name: sp2; blockState: SUBINPROGRESS; state: INPROGRESS, xid/subid/cid: 741/3/3
    INFO:  StartSubTransaction(4) name: sp3; blockState: SUBBEGIN; state: INPROGRESS, xid/subid/cid: 0/4/3
    SAVEPOINT
    postgres=*# select * from t1;
     id 
    ----
      1
      2
      3
    (3 rows)
    
    postgres=*# insert into t1 values(4);
    INSERT 0 1
    -- INFO6
    postgres=*# release savepoint sp2;
    INFO:  CommitSubTransaction(1) name: unnamed; blockState: INPROGRESS; state: INPROGRESS, xid/subid/cid: 739/1/4
    INFO:  CommitSubTransaction(2) name: sp1; blockState: SUBINPROGRESS; state: INPROGRESS, xid/subid/cid: 740/2/4
    INFO:  CommitSubTransaction(3) name: sp2; blockState: SUBRELEASE; state: INPROGRESS, xid/subid/cid: 741/3/4
    INFO:  CommitSubTransaction(4) name: sp3; blockState: SUBRELEASE; state: INPROGRESS, xid/subid/cid: 742/4/4
    INFO:  CommitSubTransaction(1) name: unnamed; blockState: INPROGRESS; state: INPROGRESS, xid/subid/cid: 739/1/4
    INFO:  CommitSubTransaction(2) name: sp1; blockState: SUBINPROGRESS; state: INPROGRESS, xid/subid/cid: 740/2/4
    INFO:  CommitSubTransaction(3) name: sp2; blockState: SUBRELEASE; state: INPROGRESS, xid/subid/cid: 741/3/4, children: 742
    RELEASE
    postgres=*# select * from t1;
     id 
    ----
      1
      2
      3
      4
    (4 rows)
    
    -- INFO7
    postgres=*# savepoint sp1;
    INFO:  StartSubTransaction(1) name: unnamed; blockState: INPROGRESS; state: INPROGRESS, xid/subid/cid: 739/1/4
    INFO:  StartSubTransaction(2) name: sp1; blockState: SUBINPROGRESS; state: INPROGRESS, xid/subid/cid: 740/2/4, children: 741 742
    INFO:  StartSubTransaction(3) name: sp1; blockState: SUBBEGIN; state: INPROGRESS, xid/subid/cid: 0/5/4
    SAVEPOINT
    postgres=*# insert into t1 values(5);
    INSERT 0 1
    -- INFO8
    postgres=*# rollback to savepoint sp1;
    INFO:  AbortSubTransaction(1) name: unnamed; blockState: INPROGRESS; state: INPROGRESS, xid/subid/cid: 739/1/5
    INFO:  AbortSubTransaction(2) name: sp1; blockState: SUBINPROGRESS; state: INPROGRESS, xid/subid/cid: 740/2/5, children: 741 742
    INFO:  AbortSubTransaction(3) name: unnamed; blockState: SUBRESTART; state: INPROGRESS, xid/subid/cid: 743/5/5
    INFO:  CleanupSubTransaction(1) name: unnamed; blockState: INPROGRESS; state: INPROGRESS, xid/subid/cid: 739/1/5
    INFO:  CleanupSubTransaction(2) name: sp1; blockState: SUBINPROGRESS; state: INPROGRESS, xid/subid/cid: 740/2/5, children: 741 742
    INFO:  CleanupSubTransaction(3) name: unnamed; blockState: SUBRESTART; state: ABORT, xid/subid/cid: 743/5/5
    INFO:  StartSubTransaction(1) name: unnamed; blockState: INPROGRESS; state: INPROGRESS, xid/subid/cid: 739/1/5
    INFO:  StartSubTransaction(2) name: sp1; blockState: SUBINPROGRESS; state: INPROGRESS, xid/subid/cid: 740/2/5, children: 741 742
    INFO:  StartSubTransaction(3) name: sp1; blockState: SUBBEGIN; state: INPROGRESS, xid/subid/cid: 0/6/5
    ROLLBACK
    postgres=*# select * from t1;
     id 
    ----
      1
      2
      3
      4
    (4 rows)
    
    -- INFO9
    postgres=*# end;
    INFO:  CommitSubTransaction(1) name: unnamed; blockState: END; state: INPROGRESS, xid/subid/cid: 739/1/5
    INFO:  CommitSubTransaction(2) name: sp1; blockState: SUBCOMMIT; state: INPROGRESS, xid/subid/cid: 740/2/5, children: 741 742
    INFO:  CommitSubTransaction(3) name: sp1; blockState: SUBCOMMIT; state: INPROGRESS, xid/subid/cid: 0/6/5
    INFO:  CommitSubTransaction(1) name: unnamed; blockState: END; state: INPROGRESS, xid/subid/cid: 739/1/5
    INFO:  CommitSubTransaction(2) name: sp1; blockState: SUBCOMMIT; state: INPROGRESS, xid/subid/cid: 740/2/5, children: 741 742
    INFO:  CommitTransaction(1) name: unnamed; blockState: END; state: INPROGRESS, xid/subid/cid: 739/1/5, children: 740 741 742
    COMMIT
    postgres=#
    

    接下来,我们对上面这10个info块逐个进行分析,如下:

    INFO0

    ...
    			"setupCommands": [
                    {
                        "description": "为 gdb 启用整齐打印",
                        "text": "-enable-pretty-printing",
                        "ignoreFailures": true
                    },
                    {
                        "description": "将反汇编风格设置为 Intel",
                        "text": "-gdb-set disassembly-flavor intel",
                        "ignoreFailures": true
                    },
                    {"text": "-gdb-set follow-fork-mode child"} ## here
                ]
    ...
    

    在这里插入图片描述

    此时的堆栈,如下:

    ShowTransactionStateRec(const char * str, TransactionState s)
    ShowTransactionState(const char * str)
    StartTransactionCommand()
    InitPostgres(const char * in_dbname, Oid dboid, const char * username, Oid useroid, bits32 flags, char * out_dbname)
    PostgresMain(const char * dbname, const char * username)
    BackendMain(char * startup_data, size_t startup_data_len)
    postmaster_child_launch(BackendType child_type, char * startup_data, size_t startup_data_len, ClientSocket * client_sock)
    BackendStartup(ClientSocket * client_sock)
    ServerLoop()
    PostmasterMain(int argc, char ** argv)
    main(int argc, char ** argv)
    
    -- 这里实际上会打印下面这样一行
    -- 没有打印的原因:现在正在建立连接的初始状态中 尚无法往client发送
    
    INFO:  StartTransaction(1) name: unnamed; blockState: DEFAULT; state: INPROGRESS, xid/subid/cid: 0/1/0
    

    在这里插入图片描述

    ## 该过程中,相关状态机的变化:
    
    state的值改变:TRANS_DEFAULT -> TRANS_START -> TRANS_INPROGRESS
    
    ## StartTransaction() 之后
    blockState的值改变:TBLOCK_DEFAULT -> TBLOCK_STARTED
    
    ## 继续:
    

    ## 此时的函数堆栈,如下:
    
    ShowTransactionStateRec(const char * str, TransactionState s)
    ShowTransactionState(const char * str)
    CommitTransaction()
    CommitTransactionCommandInternal()
    CommitTransactionCommand()
    InitPostgres(const char * in_dbname, Oid dboid, const char * username, Oid useroid, bits32 flags, char * out_dbname)
    PostgresMain(const char * dbname, const char * username)
    BackendMain(char * startup_data, size_t startup_data_len)
    postmaster_child_launch(BackendType child_type, char * startup_data, size_t startup_data_len, ClientSocket * client_sock)
    BackendStartup(ClientSocket * client_sock)
    ServerLoop()
    PostmasterMain(int argc, char ** argv)
    main(int argc, char ** argv) 
    

    在这里插入图片描述

    ## 上面INFO打印之后,相关状态机的变化:
    
    state的值改变:TRANS_INPROGRESS -> TRANS_COMMIT -> TRANS_DEFAULT
    
    ## CommitTransaction() 之后
    blockState的值改变:TBLOCK_STARTED -> TBLOCK_DEFAULT
    
    ## INFO0结束!
    

    接下来我们调试的将不再是附加postmaster进程,而是上面这个backend进程!

    INFO1

    此时函数堆栈,如下:

    ShowTransactionStateRec(const char * str, TransactionState s)
    ShowTransactionState(const char * str)
    StartTransaction()
    StartTransactionCommand()
    start_xact_command()
    exec_simple_query(const char * query_string) ## here
    PostgresMain(const char * dbname, const char * username)
    BackendMain(char * startup_data, size_t startup_data_len)
    postmaster_child_launch(BackendType child_type, char * startup_data, size_t startup_data_len, ClientSocket * client_sock)
    BackendStartup(ClientSocket * client_sock)
    ServerLoop()
    PostmasterMain(int argc, char ** argv)
    main(int argc, char ** argv)
    

    在这里插入图片描述

    ## 该过程中,相关状态机的变化:
    
    State的值改变: TRANS_DEFAULT -> TRANS_START -> TRANS_INPROGRESS
    
    ## StartTransaction() 之后:
    blockState的值改变: TBLOCK_DEFAULT->TBLOCK_STARTED
    
    ## 继续
    

    接下来是执行这个create语句!


    ## 函数堆栈,如下:
    
    ShowTransactionStateRec(const char * str, TransactionState s)
    ShowTransactionState(const char * str)
    CommitTransaction()
    CommitTransactionCommandInternal()
    CommitTransactionCommand()
    finish_xact_command() ## here
    exec_simple_query(const char * query_string)
    PostgresMain(const char * dbname, const char * username)
    BackendMain(char * startup_data, size_t startup_data_len)
    postmaster_child_launch(BackendType child_type, char * startup_data, size_t startup_data_len, ClientSocket * client_sock)
    BackendStartup(ClientSocket * client_sock)
    ServerLoop()
    PostmasterMain(int argc, char ** argv)
    main(int argc, char ** argv)
    

    在这里插入图片描述

    ## 上面INFO打印之后,相关状态机的变化:
    
    State的值改变: TRANS_INPROGRESS -> TRANS_COMMIT -> TRANS_DEFAULT
    
    ## CommitTransaction() 之后:
    blockState的值改变: TBLOCK_STARTED -> TBLOCK_DEFAULT
    
    ## INFO1结束!
    

    INFO2

    此时的函数堆栈,如下:

    ShowTransactionStateRec(const char * str, TransactionState s)
    ShowTransactionState(const char * str)
    StartTransaction() 
    StartTransactionCommand() 
    start_xact_command() 
    exec_simple_query(const char * query_string)  ## here 
    PostgresMain(const char * dbname, const char * username) 
    BackendMain(char * startup_data, size_t startup_data_len) 
    postmaster_child_launch(BackendType child_type, char * startup_data, size_t startup_data_len, ClientSocket * client_sock) 
    BackendStartup(ClientSocket * client_sock) 
    ServerLoop() 
    PostmasterMain(int argc, char ** argv) 
    main(int argc, char ** argv) 
    

    在这里插入图片描述

    ## 该过程中,相关状态机的变化:
    
    State的值改变: TRANS_DEFAULT -> TRANS_START -> TRANS_INPROGRESS
    
    ## StartTransaction() 之后:
    blockState的值改变: TBLOCK_DEFAULT -> TBLOCK_STARTED
    
    ## 继续
    

    接下来就是执行这个begin命令,再接下来的函数堆栈,如下:

    BeginTransactionBlock()
    standard_ProcessUtility(PlannedStmt * pstmt, const char * queryString, _Bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment * queryEnv, DestReceiver * dest, QueryCompletion * qc) 
    ProcessUtility(PlannedStmt * pstmt, const char * queryString, _Bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment * queryEnv, DestReceiver * dest, QueryCompletion * qc) 
    PortalRunUtility(Portal portal, PlannedStmt * pstmt, _Bool isTopLevel, _Bool setHoldSnapshot, DestReceiver * dest, QueryCompletion * qc) 
    PortalRunMulti(Portal portal, _Bool isTopLevel, _Bool setHoldSnapshot, DestReceiver * dest, DestReceiver * altdest, QueryCompletion * qc)
    PortalRun(Portal portal, long count, _Bool isTopLevel, _Bool run_once, DestReceiver * dest, DestReceiver * altdest, QueryCompletion * qc) ## here
    exec_simple_query(const char * query_string)
    ...
    
    # 在 BeginTransactionBlock() 中
    
    blockState的值改变: TBLOCK_STARTED -> TBLOCK_BEGIN
    

    CommitTransactionCommandInternal()
    CommitTransactionCommand()
    finish_xact_command()
    exec_simple_query(const char * query_string) ## here
    PostgresMain(const char * dbname, const char * username)
    BackendMain(char * startup_data, size_t startup_data_len)
    postmaster_child_launch(BackendType child_type, char * startup_data, size_t startup_data_len, ClientSocket * client_sock)
    BackendStartup(ClientSocket * client_sock)
    ServerLoop()
    PostmasterMain(int argc, char ** argv)
    main(int argc, char ** argv) 
    
    ## 在 CommitTransactionCommandInternal() 中
    
    blockState的值改变: TBLOCK_BEGIN -> TBLOCK_INPROGRESS
    

    接下来开始insert into t1 values(1);语句,首先这里也会进入到StartTransactionCommand,不过处理如下:

    // blockState的值为TBLOCK_INPROGRESS,如下:
    
    ...
    		/*
    		 * We are somewhere in a transaction block or subtransaction and
    		 * about to start a new command.  For now we do nothing, but
    		 * someday we may do command-local resource initialization. (Note
    		 * that any needed CommandCounterIncrement was done by the
    		 * previous CommitTransactionCommand.)
    		 *  
    		 * 我们在某个事务块或子事务中,即将启动一个新命令
    		 * 现在我们什么都不做,但将来我们可能会进行命令本地资源初始化
    		 * (注意,任何需要的CommandCounterIncrement都是由之前的CommitTransactionCommand完成的)
    		 */
    ...
    

    然后真正执行INSERT,继续


    之后的函数堆栈,如下:

    CommitTransactionCommandInternal() 
    CommitTransactionCommand()
    finish_xact_command() ## here
    exec_simple_query(const char * query_string) 
    ...
    
    ## 此时blockState的值为TBLOCK_INPROGRESS,仅作如下处理:
    
    CommandCounterIncrement();
    

    在这里插入图片描述

    至此,INFO2结束!


    INFO3

    接下来 savepoint sp1; 开启相关子事务,首先也会进入 如下的处理:

    StartTransactionCommand() 
    start_xact_command() 
    exec_simple_query(const char * query_string)
    ...
    

    不过由于blockState = TBLOCK_INPROGRESS,所以不做下一步处理。


    继续:

    在这里插入图片描述

    DefineSavepoint中,因为blockState = TBLOCK_INPROGRESS,处理如下:

    // src/backend/access/transam/xact.c
    
    ...
    		case TBLOCK_INPROGRESS:
    		case TBLOCK_SUBINPROGRESS:
    			/* Normal subtransaction start */
    			PushTransaction();
    			s = CurrentTransactionState;	/* changed by push */ 
    			// 上面入栈,自然 current 是指向子事务
    
    			/*
    			 * Savepoint names, like the TransactionState block itself, live
    			 * in TopTransactionContext.
    			 */
    			if (name)
    				s->name = MemoryContextStrdup(TopTransactionContext, name);
    			break;
    ...
    

    PushTransaction() 中创建了一个子事务块 TransactionStateData,自然相关状态机init 和 入栈操作 如下:

    	...
    	s->parent = p;
    	s->nestingLevel = p->nestingLevel + 1;
    	...
    	s->state = TRANS_DEFAULT;
    	s->blockState = TBLOCK_SUBBEGIN;
    	...
    	CurrentTransactionState = s;
    

    之后savepoint完成,继续


    CommitTransactionCommandInternal()
    CommitTransactionCommand()
    finish_xact_command() 
    exec_simple_query(const char * query_string) 
    ...
    

    此时因为栈顶为子事务sp1,其 blockState = TBLOCK_SUBBEGIN,如下:

    ## 在CommitTransactionCommandInternal->StartSubTransaction之中,相关状态机的变化:
    
    State的值改变: TRANS_DEFAULT -> TRANS_START -> TRANS_INPROGRESS
    

    接下来在 ShowTransactionState(const char *str) 的打印中,因为current有parent,自然打印(递归)如下:

    在这里插入图片描述

    提神醒脑一:

    ## StartSubTransaction() 之后
    
    子事务sp1的状态机值(含变化)如下:
    blockState: TBLOCK_SUBBEGIN -> TBLOCK_SUBINPROGRESS
    state: TRANS_INPROGRESS
    
    父事务的状态机值如下:
    blockState: TBLOCK_INPROGRESS
    state: TRANS_INPROGRESS
    

    至此,savepoint sp1;相关的整个执行完成,继续


    接下来insert into t1 values(2);,照常如下:

    StartTransactionCommand()
    start_xact_command()
    exec_simple_query(const char * query_string)
    ...
    

    因此此时current是sp1,相关状态如下:

    blockState:TBLOCK_SUBINPROGRESS
    state     :TRANS_INPROGRESS
    
    ...
    			/*
    			 * We are somewhere in a transaction block or subtransaction and
    			 * about to start a new command.  For now we do nothing, but
    			 * someday we may do command-local resource initialization. (Note
    			 * that any needed CommandCounterIncrement was done by the
    			 * previous CommitTransactionCommand.)
    			 *  
    			 * 我们在某个事务块或子事务中,即将启动一个新命令
    			 * 现在我们什么都不做,但将来我们可能会进行命令本地资源初始化
    			 * (注意,任何需要的CommandCounterIncrement都是由之前的CommitTransactionCommand完成的)
    			 */
    		case TBLOCK_INPROGRESS:
    		case TBLOCK_IMPLICIT_INPROGRESS:
    		case TBLOCK_SUBINPROGRESS:
    			break;
    ...
    

    接下来就是真正执行INSERT,继续


    CommitTransactionCommandInternal() 
    CommitTransactionCommand()
    finish_xact_command()
    exec_simple_query(const char * query_string) 
    ...
    

    因为blockState:TBLOCK_SUBINPROGRESS,此时的处理,如下:

    			/*
    			 * 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:
    		case TBLOCK_IMPLICIT_INPROGRESS:
    		case TBLOCK_SUBINPROGRESS:
    			CommandCounterIncrement();
    			break;
    

    至此INFO3结束!


    INFO4

    接下来savepoint sp2;继续开启相关子事务,首先也会进入 如下的处理:

    StartTransactionCommand() 
    start_xact_command() 
    exec_simple_query(const char * query_string)
    ...
    

    不过由于blockState = TBLOCK_INPROGRESS,所以不做下一步处理。


    继续:

    在这里插入图片描述

    DefineSavepoint中,因为blockState = TBLOCK_SUBINPROGRESS,处理如下:

    // src/backend/access/transam/xact.c
    
    ...
    		case TBLOCK_INPROGRESS:
    		case TBLOCK_SUBINPROGRESS:
    			/* Normal subtransaction start */
    			PushTransaction();
    			s = CurrentTransactionState;	/* changed by push */ 
    			// 上面入栈,自然 current 是指向子事务
    
    			/*
    			 * Savepoint names, like the TransactionState block itself, live
    			 * in TopTransactionContext.
    			 */
    			if (name)
    				s->name = MemoryContextStrdup(TopTransactionContext, name);
    			break;
    ...
    

    PushTransaction()中创建了一个子事务块TransactionStateData,自然相关状态机init 和 入栈操作 如下:

    	...
    	s->parent = p;
    	s->nestingLevel = p->nestingLevel + 1;
    	...
    	s->state = TRANS_DEFAULT;
    	s->blockState = TBLOCK_SUBBEGIN;
    	...
    	CurrentTransactionState = s;
    

    之后savepoint完成,继续


    CommitTransactionCommandInternal()
    CommitTransactionCommand()
    finish_xact_command() 
    exec_simple_query(const char * query_string) 
    ...
    

    此时因为栈顶为子事务sp2,其blockState = TBLOCK_SUBBEGIN,如下:

    ## 在CommitTransactionCommandInternal->StartSubTransaction之中,相关状态机的变化:
    
    State的值改变: TRANS_DEFAULT -> TRANS_START -> TRANS_INPROGRESS
    

    接下来在ShowTransactionState(const char *str)的打印中,因为current有parent,自然打印(递归)如下:

    在这里插入图片描述

    提神醒脑二:

    ## StartSubTransaction() 之后
    
    子事务sp2的状态机值(含变化)如下:
    blockState: TBLOCK_SUBBEGIN -> TBLOCK_SUBINPROGRESS
    state: TRANS_INPROGRESS
    
    子事务sp1的状态机值如下:
    blockState: TBLOCK_SUBINPROGRESS
    state: TRANS_INPROGRESS
    
    父事务的状态机值如下:
    blockState: TBLOCK_INPROGRESS
    state: TRANS_INPROGRESS
    

    至此,savepoint sp2;相关的整个执行完成,继续


    接下来insert into t1 values(3);,照常如下:

    StartTransactionCommand()
    start_xact_command()
    exec_simple_query(const char * query_string)
    ...
    

    因此此时current是sp2,相关状态如下:

    blockState:TBLOCK_SUBINPROGRESS
    state     :TRANS_INPROGRESS
    
    ...
    			/*
    			 * We are somewhere in a transaction block or subtransaction and
    			 * about to start a new command.  For now we do nothing, but
    			 * someday we may do command-local resource initialization. (Note
    			 * that any needed CommandCounterIncrement was done by the
    			 * previous CommitTransactionCommand.)
    			 *  
    			 * 我们在某个事务块或子事务中,即将启动一个新命令
    			 * 现在我们什么都不做,但将来我们可能会进行命令本地资源初始化
    			 * (注意,任何需要的CommandCounterIncrement都是由之前的CommitTransactionCommand完成的)
    			 */
    		case TBLOCK_INPROGRESS:
    		case TBLOCK_IMPLICIT_INPROGRESS:
    		case TBLOCK_SUBINPROGRESS:
    			break;
    ...
    

    接下来就是真正执行INSERT,继续


    CommitTransactionCommandInternal() 
    CommitTransactionCommand()
    finish_xact_command()
    exec_simple_query(const char * query_string) 
    ...
    

    因为blockState:TBLOCK_SUBINPROGRESS,此时的处理,如下:

    			/*
    			 * 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:
    		case TBLOCK_IMPLICIT_INPROGRESS:
    		case TBLOCK_SUBINPROGRESS:
    			CommandCounterIncrement();
    			break;
    

    至此INFO4结束!


    INFO5

    接下来savepoint sp3;继续开启相关子事务,首先也会进入 如下的处理:

    StartTransactionCommand() 
    start_xact_command() 
    exec_simple_query(const char * query_string)
    ...
    

    不过由于blockState = TBLOCK_INPROGRESS,所以不做下一步处理。


    继续:

    在这里插入图片描述

    DefineSavepoint中,因为blockState = TBLOCK_SUBINPROGRESS,处理如下:

    // src/backend/access/transam/xact.c
    
    ...
    		case TBLOCK_INPROGRESS:
    		case TBLOCK_SUBINPROGRESS:
    			/* Normal subtransaction start */
    			PushTransaction();
    			s = CurrentTransactionState;	/* changed by push */ 
    			// 上面入栈,自然 current 是指向子事务
    
    			/*
    			 * Savepoint names, like the TransactionState block itself, live
    			 * in TopTransactionContext.
    			 */
    			if (name)
    				s->name = MemoryContextStrdup(TopTransactionContext, name);
    			break;
    ...
    

    PushTransaction()中创建了一个子事务块TransactionStateData,自然相关状态机init 和 入栈操作 如下:

    	...
    	s->parent = p;
    	s->nestingLevel = p->nestingLevel + 1;
    	...
    	s->state = TRANS_DEFAULT;
    	s->blockState = TBLOCK_SUBBEGIN;
    	...
    	CurrentTransactionState = s;
    

    之后savepoint完成,继续


    CommitTransactionCommandInternal()
    CommitTransactionCommand()
    finish_xact_command() 
    exec_simple_query(const char * query_string) 
    ...
    

    此时因为栈顶为子事务sp3,其blockState = TBLOCK_SUBBEGIN,如下:

    ## 在CommitTransactionCommandInternal->StartSubTransaction之中,相关状态机的变化:
    
    State的值改变: TRANS_DEFAULT -> TRANS_START -> TRANS_INPROGRESS
    

    接下来在ShowTransactionState(const char *str)的打印中,因为current有parent,自然打印(递归)如下:

    在这里插入图片描述

    提神醒脑三:

    ## StartSubTransaction() 之后
    
    子事务sp3的状态机值(含变化)如下:
    blockState: TBLOCK_SUBBEGIN -> TBLOCK_SUBINPROGRESS
    state: TRANS_INPROGRESS
    
    子事务sp2的状态机值如下:
    blockState: TBLOCK_SUBBEGIN -> TBLOCK_SUBINPROGRESS
    state: TRANS_INPROGRESS
    
    子事务sp1的状态机值如下:
    blockState: TBLOCK_SUBINPROGRESS
    state: TRANS_INPROGRESS
    
    父事务的状态机值如下:
    blockState: TBLOCK_INPROGRESS
    state: TRANS_INPROGRESS
    

    至此,savepoint sp3;相关的整个执行完成,继续


    接下来是 select * from t1;insert into t1 values(3);,照常如下:

    StartTransactionCommand()
    start_xact_command()
    exec_simple_query(const char * query_string)
    ...
    

    因此此时current是sp3,相关状态如下:

    blockState:TBLOCK_SUBINPROGRESS
    state     :TRANS_INPROGRESS
    
    ...
    			/*
    			 * We are somewhere in a transaction block or subtransaction and
    			 * about to start a new command.  For now we do nothing, but
    			 * someday we may do command-local resource initialization. (Note
    			 * that any needed CommandCounterIncrement was done by the
    			 * previous CommitTransactionCommand.)
    			 *  
    			 * 我们在某个事务块或子事务中,即将启动一个新命令
    			 * 现在我们什么都不做,但将来我们可能会进行命令本地资源初始化
    			 * (注意,任何需要的CommandCounterIncrement都是由之前的CommitTransactionCommand完成的)
    			 */
    		case TBLOCK_INPROGRESS:
    		case TBLOCK_IMPLICIT_INPROGRESS:
    		case TBLOCK_SUBINPROGRESS:
    			break;
    ...
    

    接下来就是真正执行SELECTINSERT,继续


    CommitTransactionCommandInternal() 
    CommitTransactionCommand()
    finish_xact_command()
    exec_simple_query(const char * query_string) 
    ...
    

    因为blockState:TBLOCK_SUBINPROGRESS,此时的处理,如下:

    			/*
    			 * 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:
    		case TBLOCK_IMPLICIT_INPROGRESS:
    		case TBLOCK_SUBINPROGRESS:
    			CommandCounterIncrement();
    			break;
    

    至此INFO5结束!

    注:这里我们先后执行了SELECTINSERT,他们在具体的处理上有一处不同,如下:

    在这里插入图片描述

    前者的为假;后者为真。关于这个问题,后面我将另起一篇博客详细说明 这里不再赘述!


    INFO6

    接下来release savepoint sp2;继续相关子事务的处理(如果这个时候记不住之前事务的状态,去看提神醒脑),首先也会进入 如下的处理:

    StartTransactionCommand() 
    start_xact_command() 
    exec_simple_query(const char * query_string)
    ...
    

    不过由于blockState = TBLOCK_INPROGRESS,所以不做下一步处理。


    继续:

    ReleaseSavepoint(const char * name)
    standard_ProcessUtility(PlannedStmt * pstmt, const char * queryString, _Bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment * queryEnv, DestReceiver * dest, QueryCompletion * qc)
    ProcessUtility(PlannedStmt * pstmt, const char * queryString, _Bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment * queryEnv, DestReceiver * dest, QueryCompletion * qc)
    PortalRunUtility(Portal portal, PlannedStmt * pstmt, _Bool isTopLevel, _Bool setHoldSnapshot, DestReceiver * dest, QueryCompletion * qc) 
    PortalRunMulti(Portal portal, _Bool isTopLevel, _Bool setHoldSnapshot, DestReceiver * dest, DestReceiver * altdest, QueryCompletion * qc)
    PortalRun(Portal portal, long count, _Bool isTopLevel, _Bool run_once, DestReceiver * dest, DestReceiver * altdest, QueryCompletion * qc)
    exec_simple_query(const char * query_string)
    ...
    

    在这里插入图片描述

    如上,在ReleaseSavepoint函数中的如下循环逻辑处理:

    ...
    	/*
    	 * Mark "commit pending" all subtransactions up to the target
    	 * subtransaction.  The actual commits will happen when control gets to
    	 * CommitTransactionCommand.
    	 *  
    	 * 将所有子事务标记为“提交挂起”,直至目标子事务
    	 * 实际的提交将在控制到达CommitTransactionCommand时发生
    	 */
    	xact = CurrentTransactionState;
    	for (;;)
    	{
    		Assert(xact->blockState == TBLOCK_SUBINPROGRESS);
    		xact->blockState = TBLOCK_SUBRELEASE;
    		if (xact == target)
    			break;
    		xact = xact->parent;
    		Assert(PointerIsValid(xact));
    	}
    ...
    
    // 将 sp2的子事务 sp3 的blockState的值改变: TBLOCK_SUBINPROGRESS -> TBLOCK_SUBRELEASE
    // 和 sp2的blockState的值改变: TBLOCK_SUBINPROGRESS -> TBLOCK_SUBRELEASE
    

    继续:

    CommitTransactionCommandInternal()
    CommitTransactionCommand()
    finish_xact_command()
    exec_simple_query(const char * query_string)
    ...
    

    因为current是sp3,其blockState = TBLOCK_SUBRELEASE,后续处理,如下:

    ...
    			/*
    			 * The user issued a RELEASE command, so we end the current
    			 * subtransaction and return to the parent transaction. The parent
    			 * might be ended too, so repeat till we find an INPROGRESS
    			 * transaction or subtransaction.
    			 *  
    			 * 用户发出了RELEASE命令,因此我们结束当前子事务并返回到父事务
    			 * 父事务也可能被终止,因此重复此操作,直到找到INPROGRESS事务或子事务
    			 */
    		case TBLOCK_SUBRELEASE:
    			do
    			{
    				CommitSubTransaction();
    				s = CurrentTransactionState;	/* changed by pop */
    			} while (s->blockState == TBLOCK_SUBRELEASE);
    
    			Assert(s->blockState == TBLOCK_INPROGRESS ||
    				   s->blockState == TBLOCK_SUBINPROGRESS);
    			break;
    ...
    

    如上循环依次提交了栈顶TBLOCK_SUBRELEASE状态的子事务,首先是sp3 CommitSubTransaction的打印 和 状态修改,如下:

    INFO:  CommitSubTransaction(1) name: unnamed; blockState: INPROGRESS; state: INPROGRESS, xid/subid/cid: 739/1/4
    INFO:  CommitSubTransaction(2) name: sp1; blockState: SUBINPROGRESS; state: INPROGRESS, xid/subid/cid: 740/2/4
    INFO:  CommitSubTransaction(3) name: sp2; blockState: SUBRELEASE; state: INPROGRESS, xid/subid/cid: 752/3/4
    INFO:  CommitSubTransaction(4) name: sp3; blockState: SUBRELEASE; state: INPROGRESS, xid/subid/cid: 753/4/4
    
    sp3的 state:TRANS_INPROGRESS -> TRANS_COMMIT -> TRANS_DEFAULT
    
    以及 sp3 的 PopTransaction
    

    接下来是sp2 CommitSubTransaction的打印 和 状态修改,如下:

    INFO:  CommitSubTransaction(1) name: unnamed; blockState: INPROGRESS; state: INPROGRESS, xid/subid/cid: 739/1/4
    INFO:  CommitSubTransaction(2) name: sp1; blockState: SUBINPROGRESS; state: INPROGRESS, xid/subid/cid: 740/2/4
    INFO:  CommitSubTransaction(3) name: sp2; blockState: SUBRELEASE; state: INPROGRESS, xid/subid/cid: 752/3/4, children: 753
    
    sp2的 state:TRANS_INPROGRESS -> TRANS_COMMIT -> TRANS_DEFAULT
    
    以及 sp2 的 PopTransaction
    

    提神醒脑四:

    ## CommitSubTransaction() 之后
    
    子事务sp1的状态机值如下:
    blockState: TBLOCK_SUBINPROGRESS
    state: TRANS_INPROGRESS
    
    父事务的状态机值如下:
    blockState: TBLOCK_INPROGRESS
    state: TRANS_INPROGRESS
    

    至此,release savepoint sp2;相关的整个执行完成,继续


    接下来select * from t1;,照常如下:

    StartTransactionCommand()
    start_xact_command()
    exec_simple_query(const char * query_string)
    ...
    

    因此此时current是sp1,相关状态如下:

    blockState:TBLOCK_SUBINPROGRESS
    state     :TRANS_INPROGRESS
    
    ...
    			/*
    			 * We are somewhere in a transaction block or subtransaction and
    			 * about to start a new command.  For now we do nothing, but
    			 * someday we may do command-local resource initialization. (Note
    			 * that any needed CommandCounterIncrement was done by the
    			 * previous CommitTransactionCommand.)
    			 *  
    			 * 我们在某个事务块或子事务中,即将启动一个新命令
    			 * 现在我们什么都不做,但将来我们可能会进行命令本地资源初始化
    			 * (注意,任何需要的CommandCounterIncrement都是由之前的CommitTransactionCommand完成的)
    			 */
    		case TBLOCK_INPROGRESS:
    		case TBLOCK_IMPLICIT_INPROGRESS:
    		case TBLOCK_SUBINPROGRESS:
    			break;
    ...
    

    接下来就是真正执行SELECT,因为上面 release 实质上commit了两个事务,所以相关insert得以保留。继续:


    CommitTransactionCommandInternal() 
    CommitTransactionCommand()
    finish_xact_command()
    exec_simple_query(const char * query_string) 
    ...
    

    因为blockState:TBLOCK_SUBINPROGRESS,此时的处理,如下:

    			/*
    			 * 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:
    		case TBLOCK_IMPLICIT_INPROGRESS:
    		case TBLOCK_SUBINPROGRESS:
    			CommandCounterIncrement();
    			break;
    

    至此INFO6结束!


    INFO7

    接下来savepoint sp1;继续开启相关子事务(创建同名savepoint),首先也会进入 如下的处理:

    StartTransactionCommand() 
    start_xact_command() 
    exec_simple_query(const char * query_string)
    ...
    

    不过由于blockState = TBLOCK_INPROGRESS,所以不做下一步处理。


    继续:

    在这里插入图片描述

    DefineSavepoint中,因为blockState = TBLOCK_SUBINPROGRESS,处理如下:

    // src/backend/access/transam/xact.c
    
    ...
    		case TBLOCK_INPROGRESS:
    		case TBLOCK_SUBINPROGRESS:
    			/* Normal subtransaction start */
    			PushTransaction();
    			s = CurrentTransactionState;	/* changed by push */ 
    			// 上面入栈,自然 current 是指向子事务
    
    			/*
    			 * Savepoint names, like the TransactionState block itself, live
    			 * in TopTransactionContext.
    			 */
    			if (name)
    				s->name = MemoryContextStrdup(TopTransactionContext, name);
    			break;
    ...
    

    PushTransaction()中创建了一个子事务块TransactionStateData,自然相关状态机init 和 入栈操作 如下:

    	...
    	s->parent = p;
    	s->nestingLevel = p->nestingLevel + 1;
    	...
    	s->state = TRANS_DEFAULT;
    	s->blockState = TBLOCK_SUBBEGIN;
    	...
    	CurrentTransactionState = s;
    

    之后savepoint完成,继续


    CommitTransactionCommandInternal()
    CommitTransactionCommand()
    finish_xact_command() 
    exec_simple_query(const char * query_string) 
    ...
    

    此时因为栈顶为子事务第二个 sp1,其blockState = TBLOCK_SUBBEGIN,如下:

    ## 在CommitTransactionCommandInternal->StartSubTransaction之中,相关状态机的变化:
    
    State的值改变: TRANS_DEFAULT -> TRANS_START -> TRANS_INPROGRESS
    

    接下来在ShowTransactionState(const char *str)的打印中,因为current有parent,自然打印(递归)如下:

    在这里插入图片描述

    提神醒脑五:

    ## CommitTransactionCommandInternal -> StartSubTransaction() 之后
    
    子事务sp1的状态机值(含变化)如下:
    blockState: TBLOCK_SUBBEGIN -> TBLOCK_SUBINPROGRESS
    state: TRANS_INPROGRESS
    
    子事务sp1的状态机值如下:
    blockState: TBLOCK_SUBINPROGRESS
    state: TRANS_INPROGRESS
    
    父事务的状态机值如下:
    blockState: TBLOCK_INPROGRESS
    state: TRANS_INPROGRESS
    

    至此,savepoint sp1;相关的整个执行完成,继续


    接下来insert into t1 values(5);,照常如下:

    StartTransactionCommand()
    start_xact_command()
    exec_simple_query(const char * query_string)
    ...
    

    因此此时current是第二个 sp1,相关状态如下:

    blockState:TBLOCK_SUBINPROGRESS
    state     :TRANS_INPROGRESS
    
    ...
    			/*
    			 * We are somewhere in a transaction block or subtransaction and
    			 * about to start a new command.  For now we do nothing, but
    			 * someday we may do command-local resource initialization. (Note
    			 * that any needed CommandCounterIncrement was done by the
    			 * previous CommitTransactionCommand.)
    			 *  
    			 * 我们在某个事务块或子事务中,即将启动一个新命令
    			 * 现在我们什么都不做,但将来我们可能会进行命令本地资源初始化
    			 * (注意,任何需要的CommandCounterIncrement都是由之前的CommitTransactionCommand完成的)
    			 */
    		case TBLOCK_INPROGRESS:
    		case TBLOCK_IMPLICIT_INPROGRESS:
    		case TBLOCK_SUBINPROGRESS:
    			break;
    ...
    

    接下来就是真正执行INSERT,继续


    CommitTransactionCommandInternal() 
    CommitTransactionCommand()
    finish_xact_command()
    exec_simple_query(const char * query_string) 
    ...
    

    因为blockState:TBLOCK_SUBINPROGRESS,此时的处理,如下:

    			/*
    			 * 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:
    		case TBLOCK_IMPLICIT_INPROGRESS:
    		case TBLOCK_SUBINPROGRESS:
    			CommandCounterIncrement();
    			break;
    

    至此INFO7结束!


    INFO8

    接下来rollback to savepoint sp1;继续相关子事务的处理(如果这个时候记不住之前事务的状态,继续去看提神醒脑),首先也会进入 如下的处理:

    StartTransactionCommand() 
    start_xact_command() 
    exec_simple_query(const char * query_string)
    ...
    

    不过由于blockState = TBLOCK_INPROGRESS,所以不做下一步处理。


    继续:

    RollbackToSavepoint(const char * name)
    standard_ProcessUtility(PlannedStmt * pstmt, const char * queryString, _Bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment * queryEnv, DestReceiver * dest, QueryCompletion * qc)
    ProcessUtility(PlannedStmt * pstmt, const char * queryString, _Bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment * queryEnv, DestReceiver * dest, QueryCompletion * qc) 
    PortalRunUtility(Portal portal, PlannedStmt * pstmt, _Bool isTopLevel, _Bool setHoldSnapshot, DestReceiver * dest, QueryCompletion * qc) 
    PortalRunMulti(Portal portal, _Bool isTopLevel, _Bool setHoldSnapshot, DestReceiver * dest, DestReceiver * altdest, QueryCompletion * qc)
    PortalRun(Portal portal, long count, _Bool isTopLevel, _Bool run_once, DestReceiver * dest, DestReceiver * altdest, QueryCompletion * qc)
    exec_simple_query(const char * query_string)
    ...
    

    看一下相关的处理,如下:

    ...
    	for (target = s; PointerIsValid(target); target = target->parent)
    	{
    		if (PointerIsValid(target->name) && strcmp(target->name, name) == 0)
    			break;
    	}
    ...
    	/*
    	 * Mark "abort pending" all subtransactions up to the target
    	 * subtransaction.  The actual aborts will happen when control gets to
    	 * CommitTransactionCommand.
    	 *  
    	 * 将所有子事务标记为“中止挂起”,直至目标子事务
    	 * 实际的中止将在控制到达CommitTransactionCommand时发生
    	 */
    	xact = CurrentTransactionState;
    	for (;;)
    	{
    		if (xact == target)
    			break;
    		if (xact->blockState == TBLOCK_SUBINPROGRESS)
    			xact->blockState = TBLOCK_SUBABORT_PENDING;
    		else if (xact->blockState == TBLOCK_SUBABORT)
    			xact->blockState = TBLOCK_SUBABORT_END;
    		else
    			elog(FATAL, "RollbackToSavepoint: unexpected state %s",
    				 BlockStateAsString(xact->blockState));
    		xact = xact->parent;
    		Assert(PointerIsValid(xact));
    	}
    
    	/* And mark the target as "restart pending" */
    	if (xact->blockState == TBLOCK_SUBINPROGRESS)
    		xact->blockState = TBLOCK_SUBRESTART;
    	else if (xact->blockState == TBLOCK_SUBABORT)
    		xact->blockState = TBLOCK_SUBABORT_RESTART;
    ...
    

    虽然这里有两个sp1,但是rollback to savepoint sp1;寻找是倒着进行的,自然将要被回滚的就是第二次的savepoint。这里将其blockState TBLOCK_SUBINPROGRESS -> TBLOCK_SUBRESTART


    继续:

    CommitTransactionCommandInternal()
    CommitTransactionCommand()
    finish_xact_command()
    exec_simple_query(const char * query_string)
    ...
    

    当前current是第二个sp1,其blockState:TBLOCK_SUBRESTART,相关处理如下:

    ...
    			/*
    			 * The current subtransaction is the target of a ROLLBACK TO
    			 * command.  Abort and pop it, then start a new subtransaction
    			 * with the same name.
    			 *  
    			 * 当前子事务是ROLLBACK TO命令的目标
    			 * 中止并弹出它,然后启动具有相同名称的新子事务
    			 */
    		case TBLOCK_SUBRESTART:
    			{
    				char	   *name;
    				int			savepointLevel;
    
    				/* save name and keep Cleanup from freeing it */
    				name = s->name;
    				s->name = NULL;
    				savepointLevel = s->savepointLevel;
    
    				AbortSubTransaction();
    				CleanupSubTransaction();
    
    				DefineSavepoint(NULL);
    				s = CurrentTransactionState;	/* changed by push */
    				s->name = name;
    				s->savepointLevel = savepointLevel;
    
    				/* This is the same as TBLOCK_SUBBEGIN case */
    				Assert(s->blockState == TBLOCK_SUBBEGIN);
    				StartSubTransaction();
    				s->blockState = TBLOCK_SUBINPROGRESS;
    			}
    			break;
    ...
    

    在这里插入图片描述

    这里AbortSubTransaction的打印如上所示,其中:

    state:TRANS_INPROGRESS -> TRANS_ABORT
    

    在这里插入图片描述

    这里CleanupSubTransaction的打印如上所示,其中:

    state:TRANS_ABORT -> TRANS_DEFAULT
    
    以及 PopTransaction
    

    PushTransaction()
    DefineSavepoint(const char * name)
    CommitTransactionCommandInternal()
    CommitTransactionCommand()
    finish_xact_command()
    exec_simple_query(const char * query_string)
    ...
    

    DefineSavepoint之中,(栈内就剩两个事务),current是sp1 其blockState:TBLOCK_SUBINPROGRESS,如下:

    PushTransaction()中创建了一个子事务块TransactionStateData,自然相关状态机init 和 入栈操作 如下:

    	...
    	s->parent = p; // sp1
    	s->nestingLevel = p->nestingLevel + 1;
    	...
    	s->state = TRANS_DEFAULT;
    	s->blockState = TBLOCK_SUBBEGIN;
    	...
    	CurrentTransactionState = s;
    

    之后重新命令为sp1(指的是 第二个sp1),然后就可以StartSubTransaction,如下:

    state:TRANS_DEFAULT -> TRANS_START -> TRANS_INPROGRESS
    

    该过程的打印,如下:

    在这里插入图片描述

    StartSubTransaction之后,新的sp1的 blockState :TBLOCK_SUBBEGIN -> TBLOCK_SUBINPROGRESS

    注:在整个过程中 旧的sp1(第二个)被AbortSubTransaction,然后又StartSubTransaction同名的sp1。此时的提神醒脑如下:

    提神醒脑六:

    ## CommitTransactionCommandInternal -> StartSubTransaction() 之后
    
    子事务sp1的状态机值(含变化)如下:
    blockState: TBLOCK_SUBBEGIN -> TBLOCK_SUBINPROGRESS
    state: TRANS_INPROGRESS
    
    -- 下面这个是没有的(被abotr && pop),写在这里 只是为了方便理解
    子事务sp1的状态机值如下:
    blockState: TBLOCK_SUBBEGIN -> TBLOCK_SUBINPROGRESS
    state: TRANS_INPROGRESS
    
    子事务sp1的状态机值如下:
    blockState: TBLOCK_SUBINPROGRESS
    state: TRANS_INPROGRESS
    
    父事务的状态机值如下:
    blockState: TBLOCK_INPROGRESS
    state: TRANS_INPROGRESS
    

    至此rollback to savepoint sp1;,结束


    接下来select * from t1;,相关 不再赘述。上面我们总共insert 5次,其中第5个被回滚。

    注:后面我再另起文档重点介绍一下被回滚的insert 5的可见性。

    至此,INFO8结束


    INFO9

    直接上干货,如下:

    EndTransactionBlock(_Bool chain)
    standard_ProcessUtility(PlannedStmt * pstmt, const char * queryString, _Bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment * queryEnv, DestReceiver * dest, QueryCompletion * qc)
    ProcessUtility(PlannedStmt * pstmt, const char * queryString, _Bool readOnlyTree, ProcessUtilityContext context, ParamListInfo params, QueryEnvironment * queryEnv, DestReceiver * dest, QueryCompletion * qc)
    PortalRunUtility(Portal portal, PlannedStmt * pstmt, _Bool isTopLevel, _Bool setHoldSnapshot, DestReceiver * dest, QueryCompletion * qc) 
    PortalRunMulti(Portal portal, _Bool isTopLevel, _Bool setHoldSnapshot, DestReceiver * dest, DestReceiver * altdest, QueryCompletion * qc)
    PortalRun(Portal portal, long count, _Bool isTopLevel, _Bool run_once, DestReceiver * dest, DestReceiver * altdest, QueryCompletion * qc)
    exec_simple_query(const char * query_string)
    ...
    
    父
    	sp1
    		sp1
    

    接下来按照反顺序,依次commit,如下:

    			/*
    			 * We are in a live subtransaction block.  Set up to subcommit all
    			 * open subtransactions and then commit the main transaction.
    			 *  
    			 * 我们在一个活动子事务块中
    			 * 设置为提交所有打开的子事务,然后提交主事务。
    			 */
    		case TBLOCK_SUBINPROGRESS:
    			while (s->parent != NULL)
    			{
    				if (s->blockState == TBLOCK_SUBINPROGRESS)
    					s->blockState = TBLOCK_SUBCOMMIT;
    				else
    					elog(FATAL, "EndTransactionBlock: unexpected state %s",
    						 BlockStateAsString(s->blockState));
    				s = s->parent;
    			}
    			if (s->blockState == TBLOCK_INPROGRESS)
    				s->blockState = TBLOCK_END;
    			else
    				elog(FATAL, "EndTransactionBlock: unexpected state %s",
    					 BlockStateAsString(s->blockState));
    			result = true;
    			break;
    
    sp1 blockState: TBLOCK_SUBINPROGRESS -> TBLOCK_SUBCOMMIT
    
    	sp1 blockState: TBLOCK_SUBINPROGRESS -> TBLOCK_SUBCOMMIT
    
    		父 blockState: TBLOCK_INPROGRESS -> TBLOCK_END
    

    继续:

    CommitTransactionCommandInternal()
    CommitTransactionCommand()
    finish_xact_command()
    exec_simple_query(const char * query_string)
    ...
    

    当前current的blockState:TBLOCK_SUBCOMMIT,相关处理如下:

    ...
    			/*
    			 * The user issued a COMMIT, so we end the current subtransaction
    			 * hierarchy and perform final commit. We do this by rolling up
    			 * any subtransactions into their parent, which leads to O(N^2)
    			 * operations with respect to resource owners - this isn't that
    			 * bad until we approach a thousands of savepoints but is
    			 * necessary for correctness should after triggers create new
    			 * resource owners.
    			 *  
    			 * 用户发出了COMMIT,因此我们结束当前的子事务层次结构并执行最后的COMMIT
    			 * 我们通过将任何子事务卷到它们的父事务中来实现这一点,
    			 * 这导致了对资源所有者的O(N^2)次操作——在我们接近数千个保存点之前,这并不是那么糟糕,但是在触发器创建新的资源所有者之后,这对于正确性是必要的。
    			 */
    		case TBLOCK_SUBCOMMIT:
    			do
    			{
    				CommitSubTransaction();
    				s = CurrentTransactionState;	/* changed by pop */
    			} while (s->blockState == TBLOCK_SUBCOMMIT);
    			/* If we had a COMMIT command, finish off the main xact too */
    			if (s->blockState == TBLOCK_END)
    			{
    				Assert(s->parent == NULL);
    				CommitTransaction();
    				s->blockState = TBLOCK_DEFAULT;
    				if (s->chain)
    				{
    					StartTransaction();
    					s->blockState = TBLOCK_INPROGRESS;
    					s->chain = false;
    					RestoreTransactionCharacteristics(&savetc);
    				}
    			}
    			else if (s->blockState == TBLOCK_PREPARE)
    			{
    				Assert(s->parent == NULL);
    				PrepareTransaction();
    				s->blockState = TBLOCK_DEFAULT;
    			}
    			else
    				elog(ERROR, "CommitTransactionCommand: unexpected state %s",
    					 BlockStateAsString(s->blockState));
    			break;
    ...
    

    sp1的CommitSubTransaction打印及相关状态改变,如下:

    postgres=*# end;
    INFO:  CommitSubTransaction(1) name: unnamed; blockState: END; state: INPROGRESS, xid/subid/cid: 739/1/5
    INFO:  CommitSubTransaction(2) name: sp1; blockState: SUBCOMMIT; state: INPROGRESS, xid/subid/cid: 740/2/5, children: 752 753
    INFO:  CommitSubTransaction(3) name: sp1; blockState: SUBCOMMIT; state: INPROGRESS, xid/subid/cid: 0/6/5
    
    state :TRANS_INPROGRESS -> TRANS_COMMIT -> TRANS_DEFAULT
    
    以及 PopTransaction
    

    sp1的CommitSubTransaction打印及相关状态改变,如下:

    postgres=*# end;
    INFO:  CommitSubTransaction(1) name: unnamed; blockState: END; state: INPROGRESS, xid/subid/cid: 739/1/5
    INFO:  CommitSubTransaction(2) name: sp1; blockState: SUBCOMMIT; state: INPROGRESS, xid/subid/cid: 740/2/5, children: 752 753
    INFO:  CommitSubTransaction(3) name: sp1; blockState: SUBCOMMIT; state: INPROGRESS, xid/subid/cid: 0/6/5
    INFO:  CommitSubTransaction(1) name: unnamed; blockState: END; state: INPROGRESS, xid/subid/cid: 739/1/5 // here
    INFO:  CommitSubTransaction(2) name: sp1; blockState: SUBCOMMIT; state: INPROGRESS, xid/subid/cid: 740/2/5, children: 752 753 // here
    
    state :TRANS_INPROGRESS -> TRANS_COMMIT -> TRANS_DEFAULT
    
    以及 PopTransaction
    

    父事务的CommitTransaction打印及相关状态改变,如下:

    postgres=*# end;
    INFO:  CommitSubTransaction(1) name: unnamed; blockState: END; state: INPROGRESS, xid/subid/cid: 739/1/5
    INFO:  CommitSubTransaction(2) name: sp1; blockState: SUBCOMMIT; state: INPROGRESS, xid/subid/cid: 740/2/5, children: 752 753
    INFO:  CommitSubTransaction(3) name: sp1; blockState: SUBCOMMIT; state: INPROGRESS, xid/subid/cid: 0/6/5
    INFO:  CommitSubTransaction(1) name: unnamed; blockState: END; state: INPROGRESS, xid/subid/cid: 739/1/5 
    INFO:  CommitSubTransaction(2) name: sp1; blockState: SUBCOMMIT; state: INPROGRESS, xid/subid/cid: 740/2/5, children: 752 753 
    INFO:  CommitTransaction(1) name: unnamed; blockState: END; state: INPROGRESS, xid/subid/cid: 739/1/5, children: 740 752 753 // here
    
    state :TRANS_INPROGRESS -> TRANS_COMMIT -> TRANS_DEFAULT
    blockState: TBLOCK_END -> TBLOCK_DEFAULT
    

    至此 INFO9 结束!!!

  • 相关阅读:
    点到直线的距离直线的交点及夹角
    大语言模型之十 SentencePiece
    天工杂志天工杂志社天工编辑部2022年第25期目录
    ant-design国际化扩展新语言
    Hadoop实训有谁会做有尝
    阿里也出手了!Spring CloudAlibaba AI问世了
    如何根据不同仪器选择适合的电源模块?
    事务管理需要了解的前置知识
    华为机试真题 C++ 实现【分班】
    C# BinaryFormatter序列化后的文件格式
  • 原文地址:https://blog.csdn.net/weixin_43949535/article/details/139631954