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


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

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


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


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

  • 文章快速说明索引
  • 重点数据结构说明
  • 源码解析案例分解
    • INFO0
    • INFO1
    • INFO2
    • INFO3
    • INFO4
    • INFO5
    • INFO6
    • INFO7
    • INFO8
    • INFO9



文章快速说明索引

学习目标:

做数据库内核开发久了就会有一种 少年得志,年少轻狂 的错觉,然鹅细细一品觉得自己其实不算特别优秀 远远没有达到自己想要的。也许光鲜的表面掩盖了空洞的内在,每每想到于此,皆有夜半临渊如履薄冰之感。为了睡上几个踏实觉,即日起 暂缓其他基于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 结束!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/728696.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Kubernetes部署Kanboard看板管理平台

【云原生】Kubernetes部署Kanboard项目管理平台 文章目录 【云原生】Kubernetes部署Kanboard项目管理平台介绍资源列表基础环境一、检查k8s环境1.1、检查工作节点状态1.2、检查系统pod状态 二、编辑kanboard.yaml文件2.1、创建项目目录2.2、编辑kanboard.yaml文件 三、部署Kanb…

Hadoop三大组件原理详解:hdfs-yarn-MapReduce(第9天)

系列文章目录 一、HDFS读写原理【重点】 二、YARN提交mr流程【重点】 三、MapReduce计算流程【重点】 文章目录 系列文章目录前言一、HDFS读写原理[面试]1、HDFS数据写入解析2、HDFS数据读取解析 二、YARN提交mr流程[面试]1. YARN提交mr过程解析 三、MapReduce计算流程[面试]1…

探索监管沙箱在金融科技行业中的应用

一、引言 随着金融科技的快速发展&#xff0c;传统金融机构与科技企业之间的竞争也日趋激烈。为了平衡金融科技创新与风险防控&#xff0c;各国监管机构纷纷引入监管沙箱&#xff08;Regulatory Sandbox&#xff09;机制。监管沙箱作为一个受监督的安全测试区&#xff0c;允许金…

MySQL 面试突击指南:核心知识点解析1

MySQL中有哪些存储引擎? InnoDB存储引擎 InnoDB是MySQL的默认事务型引擎,也是最重要、使用最广泛的存储引擎,设计用于处理大量短期事务。 MyISAM存储引擎 在MySQL 5.1及之前版本,MyISAM是默认的存储引擎。它提供了全文索引、压缩、空间函数(GIS)等特性,但不支持事务和…

台积电(TSMC)正在探索采用新型先进芯片封装技术

台积电&#xff08;TSMC&#xff09;正在探索采用新型先进芯片封装技术&#xff0c;使用类似面板的矩形基板&#xff0c;以应对日益增长的先进多芯片组处理器需求。据日经亚洲报道&#xff0c;这项开发仍处于早期阶段&#xff0c;可能需要数年时间才能商业化&#xff0c;但如果…

Python酷库之旅-第三方库openpyxl(01)

目录 一、 openpyxl库的由来 1、背景 2、起源 3、发展 4、特点 4-1、支持.xlsx格式 4-2、读写Excel文件 4-3、操作单元格 4-4、创建和修改工作表 4-5、样式设置 4-6、图表和公式 4-7、支持数字和日期格式 二、openpyxl库的优缺点 1、优点 1-1、支持现代Excel格式…

【C语言 || 数据结构】二叉树

文章目录 前言 二叉树1.树1.1树的定义1.2 树的结构 2.特殊的树&#xff08;二叉树&#xff09;2.1 二叉树的概念2.2 特殊的二叉树2.3 二叉树的储存2.3.1 顺序储存二叉树2.3.2 链表储存二叉树 2.4 二叉树的遍历2.4.1 二叉树的中序遍历2.4.2 二叉树的前序遍历2.4.3 二叉树的后序遍…

【React】使用Token做路由权限控制

在components/AuthRoute/index.js中 import { getToken } from /utils import { Navigate } from react-router-domconst AuthRoute ({ children }) > {const isToken getToken()if (isToken) {return <>{children}</>} else {return <Navigate to"/…

Solr9 如何使用 DIH 读取数据库索引数据

使用 Solr 9 中的数据导入处理程序&#xff08;DIH&#xff09; DIH&#xff08;Data Import Handler&#xff09;提供了一种可配置的方式向 Solr 中导入数据。 从 Solr 9 开始&#xff0c;数据导入处理程序&#xff08;DIH&#xff09;已经不再直接包含在 Solr 中&#xff0c…

【Linux】关于在华为云中开放了端口后仍然无法访问的问题

已在安全组中添加规则: 通过指令: netstat -nltp | head -2 && netstat -nltp | grep 8080 运行结果: 可以看到服务器确实处于监听状态了. 通过指令 telnet 公网ip port 也提示: "正在连接xxx.xx.xx.xxx...无法打开到主机的连接。 在端口 8080: 连接失败"…

[WTL/Win32]_[中级]_[MVP架构在实际项目中的应用]

场景 在开发Windows和macOS的界面软件时&#xff0c;Windows用的是WTL/Win32技术&#xff0c;而macOS用的是Cocoa技术。而两种技术的本地语言一个主打是C,另一个却是Object-c。界面软件的源码随着项目功能增多而增多&#xff0c;这就会给同步Windows和macOS的功能造成很大负担…

数据驱动制造:EMQX ECP 指标监测功能增强生产透明度

迈向未来的工业生产&#xff0c;需要的不仅是自动化&#xff0c;更是智能化。如果工业企业的管理者能够实时监测每一生产环节的设备运行状态&#xff0c;每一数据点位情况&#xff0c;洞察和优化每一步生产流程&#xff0c;他们将能够做出更精准的决策&#xff0c;提高生产效率…

记录SpringBoot启动报错解决

记录SpringBoot启动报错解决 报错现场 Failed to configure a DataSource: url attribute is not specified and no embedded datasource could be configured. Reason: Failed to determine a suitable driver class Action: Consider the following:If you want an embedde…

紧凑型计算微型仿生复眼

欢迎关注&#xff1a;GZH《光场视觉》 图1 研制的计算微型复眼的成像原理 1. 导读 微型曲面复眼由于具有大视场成像、大景深成像、体积较小的优势&#xff0c;在机器视觉、无人机导航、生物灵感机器人等领域引起了广泛关注。然而&#xff0c;传统的微型曲面复眼存在设计/加工…

44、基于深度学习的癌症检测(matlab)

1、基于深度学习的癌症检测原理及流程 基于深度学习的癌症检测是利用深度学习算法对医学影像数据进行分析和诊断&#xff0c;以帮助医生准确地检测癌症病变。其原理和流程主要包括以下几个步骤&#xff1a; 数据采集&#xff1a;首先需要收集包括X光片、CT扫描、MRI等医学影像…

Shiro721 反序列化漏洞(CVE-2019-12422)

目录 Shiro550和Shiro721的区别 判断是否存在漏洞 漏洞环境搭建 漏洞利用 利用Shiro检测工具 利用Shiro综综合利用工具 这一篇还是参考别的师傅的好文章学习Shiro的反序列化漏洞 上一篇也是Shiro的反序列化漏洞&#xff0c;不同的是一个是550一个是721&#xff0c;那么这…

基于SSM+Jsp的水果销售管理网站

开发语言&#xff1a;Java框架&#xff1a;ssm技术&#xff1a;JSPJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包…

技术速递|Java on Azure Tooling 5月更新 - Java 对 Azure 容器应用程序的入门指南支持

作者&#xff1a;Jialuo Gan 排版&#xff1a;Alan Wang 大家好&#xff0c;欢迎阅读 Java on Azure 工具 5 月份更新。在本次更新中&#xff0c;我们将介绍 Java 在 Azure 上的容器应用程序的入门指南。希望您喜欢这些更新&#xff0c;并享受使用 Azure 工具包的流畅体验。请下…

在4面体空间内2点结构占比

有一个4面体状空间&#xff0c;由3层甲烷状分子堆积而成&#xff0c;单个甲烷4面体边长10. 内有30个点&#xff0c;在30个点中取2点&#xff0c;有30*29/2435种取法。这里要求两个点的距离必须为6.123 在435个结构中只有40个符合要求 序数 结构 序数 结构 3 1 282 3 7…

如何利用AI大模型设计电机本体?

一、背景 AI在电机本体设计中的应用正逐渐成为提升设计效率、优化性能和降低成本的重要手段。通过深度学习、机器学习、计算机辅助设计&#xff08;CAD&#xff09;和仿真技术的结合&#xff0c;AI能够帮助工程师更快速准确地完成电机的设计与优化工作。以下是AI在电机本体设计…