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)));elseShowTransactionStateRec(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 voidShowTransactionState(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 
----123
(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 
----1234
(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 
----1234
(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/4sp3的 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: 753sp2的 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;elseelog(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; // sp1s->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)
...
父sp1sp1

接下来按照反顺序,依次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;elseelog(FATAL, "EndTransactionBlock: unexpected state %s",BlockStateAsString(s->blockState));s = s->parent;}if (s->blockState == TBLOCK_INPROGRESS)s->blockState = TBLOCK_END;elseelog(FATAL, "EndTransactionBlock: unexpected state %s",BlockStateAsString(s->blockState));result = true;break;
sp1 blockState: TBLOCK_SUBINPROGRESS -> TBLOCK_SUBCOMMITsp1 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;}elseelog(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/5state :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 // herestate :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 // herestate :TRANS_INPROGRESS -> TRANS_COMMIT -> TRANS_DEFAULT
blockState: TBLOCK_END -> TBLOCK_DEFAULT

至此 INFO9 结束!!!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mac怎么压缩pdf文件,苹果电脑怎么压缩pdf文件大小

在当今数字化时代&#xff0c;PDF文件已成为广泛使用的文档格式之一。然而&#xff0c;PDF 文件可能会因其包含的图像、图形和其他元素而导致文件较大&#xff0c;这可能会影响文件的传输、存储和共享。因此&#xff0c;对 PDF 文件进行压缩以减小其文件大小是很有必要的。今天…

【机器学习 复习】第3章 K-近邻算法

一、概念 1.K-近邻算法&#xff1a;也叫KNN 分类 算法&#xff0c;其中的N是 邻近邻居NearestNeighbor的首字母。 &#xff08;1&#xff09;其中K是特征值&#xff0c;就是选择离某个预测的值&#xff08;例如预测的是苹果&#xff0c;就找个苹果&#xff09;最近的几个值&am…

OCC异常处理机制理解

1.目的 异常处理提供了一种将控制权从正在执行的程序中的给定点转移到与先前执行的另一点关联的异常处理程序的方法。在各种错误条件下引发异常&#xff0c;该异常会中断其正常执行并将控制权传递给捕获此异常的处理程序&#xff0c;以保护软件质量。OCC作为开源的几何算法库&…

CV预测:快速使用DenseNet神经网络

AI预测相关目录 AI预测流程&#xff0c;包括ETL、算法策略、算法模型、模型评估、可视化等相关内容 最好有基础的python算法预测经验 EEMD策略及踩坑VMD-CNN-LSTM时序预测对双向LSTM等模型添加自注意力机制K折叠交叉验证optuna超参数优化框架多任务学习-模型融合策略Transform…

如何使用nginx部署https网站(亲测可行)

公司本来有网站sqlynx.com是http运行的&#xff0c;但因为产品出海&#xff0c;基本上都要求使用https&#xff0c;但又需要兼容已有的http服务&#xff0c;所以我自己尝试做了一次https的部署&#xff0c;目前是正常可用的。 目录 步骤 1&#xff1a;安装 Nginx 步骤 2&…

国产24位I2S输入+192kHz立体声DAC音频数模转换器CJC4344

CJC4344是一款立体声数模转换芯片&#xff0c;内含插值滤波器、multi bit数模转换器、输出模拟滤波器。CJC4344系列支持大部分的音频数据格式。CJC4344基于一个带线性模拟低通滤波器的四阶multi-bitΔ-Σ调制器&#xff0c;而且本芯片可以通过检测信号频率和主时钟频率&#xf…

vi/vim使用命令

你是否在编辑文件时以为键盘坏了&#xff0c;为什么不能删除呢&#xff0c;为什么不能敲代码呢&#xff0c;当你初识vi&#xff0c;会觉得这个东西设计很难用&#xff0c;这篇教程带你熟练得用上这款经典的工具&#xff0c;当你熟练了这款工具就会真正体会到高效率打码 Vi 是在…

南京邮电大学计算机网络实验二(网络路由器配置RIP协议)

文章目录 一、 实验目的和要求二、 实验环境(实验设备)三、 实验步骤四、实验小结&#xff08;包括问题和解决方法、心得体会、意见与建议等&#xff09;五、报告资源 一、 实验目的和要求 掌握思科路由器的运行过程&#xff0c;掌握思科路由器的硬件连线与接口&#xff0c;掌…

模式分解的概念(下)-无损连接分解的与保持函数依赖分解的定义和判断、损失分解

一、无损连接分解 1、定义 2、检验一个分解是否是无损连接分解的算法 输入与输出 输入&#xff1a; 关系模式R&#xff08;U&#xff0c;F&#xff09;&#xff0c;F是最小函数依赖集 R上的一个分解 输出&#xff1a; 判断分解是否为无损连接分解 &#xff08;1&#x…

15-JS封装:入口函数

目录 1 模块化的基本结构 2 编写封装里的jQuery函数 2.1 对象本身上添加css方法 2.2 对象原型上添加css方法 2.3 自定义构造函数 2.4 优化1-伪数组 2.5 优化2-原型链 2.6 简化代码 需求&#xff1a;给页面中所有的div设置字体颜色为红色 jQuery封装&#xff1a;$("…

1. zookeeper分布式协调者

zookeeper分布协调者 一、zookeeper介绍1、软件设计架构1.1 单体架构1.2 SOA架构/分布式1.3 微服务架构 二、zookeeper角色1、角色2、选举机制3、znode类型 三、zookeeper集群部署1、环境规划2、安装jdk3、安装配置zookeeper3.1 安装zookeeper3.2 编辑配置文件3.3 创建myid文件…

基于深度学习视觉算法的多模型文件融合检测系统设计与实现及优化(工人姿态检测+安全帽佩戴检测系统)

1,融合pose.pt(姿态检测)+(安全帽佩戴检测)效果图 实时检测优化后FPS可达20+ 2,原理介绍 YOLOv5是目前应用广泛的目标检测算法之一,其主要结构分为两个部分:骨干网络和检测头。 输入(Input): YOLOv5的输入是一张RGB图像,它可以具有不同的分辨率,但通常为416x416或5…

《精通嵌入式Linux编程》——解锁嵌入式Linux开发的无限可能

文章目录 &#x1f4d1;前言一、书籍概览与作者风采二、内容详解与特色亮点2.1 嵌入式Linux基础与入门2.2 系统编程与内核探索2.3 驱动开发与实战演练2.4 内存管理与性能优化2.5 系统调试与性能提升2.6 综合项目实践与案例分析 三、书籍价值与应用展望 &#x1f4d1;前言 在当今…

基于java+springboot+vue实现的便利店信息管理系统(文末源码+Lw)239

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本华府便利店信息管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的…

WebSocket实现消息实时通知

参考文档&#xff1a;万字长文&#xff0c;一篇吃透WebSocket&#xff1a;概念、原理、易错常识、动手实践、WebSocket 教程 1 背景 有一个需求&#xff0c;需要实现实时通信的功能&#xff0c;如果有新消息&#xff0c;后端会主动发送请求告知前端有新消息&#xff0c;需要前…

Day 44 Ansible自动化运维

Ansible自动化运维 几种常用运维工具比较 ​ Puppet ​ —基于 Ruby 开发,采用 C/S 架构,扩展性强,基于 SSL,远程命令执行相对较弱ruby ​ SaltStack ​ —基于 Python 开发,采用 C/S 架构,相对 puppet 更轻量级,配置语法使用 YAML,使得配置脚本更简单 ​ Ansible ​ —基于 …

海康充电桩报文校验TCP校验和

1 TCP校验文档校验文档要求&#xff1a; 校验码描述 校验码计算范围包含包头标识、消息头和消息体&#xff0c;校验算法采用 TCP 和校验&#xff0c;具体规则如下。 将待校验的所有数据分为 16 位的字&#xff08;大端序&#xff09;&#xff0c;如果总长度为奇数个字节&#…

测试的基础知识大全【测试概念、分类、模型、流程、测试用例书写、用例设计、Bug、基础功能测试实战】

测试基础笔记 Day01阶段⽬标⼀、测试介绍⼆、测试常⽤分类2.1 阶段划分单元测试集成测试系统测试验收测试 2.2 代码可⻅度划分⿊盒测试&#xff1a;主要针对功能&#xff08;阶段划分->系统测试&#xff09;灰盒测试&#xff1a;针对接⼝测试&#xff08;阶段划分->集成测…

【CPP】选择排序:冒泡排序、快速排序

目录 1.冒泡排序简介代码分析 2.快速排序2.1霍尔版本简介代码分析 2.2挖坑版本2.3前后指针版本2.4非递归的快排思路代码 什么是交换排序&#xff1f; 基本思想&#xff1a;所谓 交换&#xff0c;就是根据序列中两个记录键值的比较结果来对换这两个记录在序列中的位置&#xff0…

Photoshop揭秘:图像处理领域的领军软件

Photoshop 是一款由 Adobe 企业开发的图像处理软件&#xff0c;也被大家简称为 PS。在广告设计、摄影后期、数字绘画、网页设计等各个领域都得到了广泛的应用&#xff0c;是目前业界最受欢迎的图像处理软件之一。作为一款图像处理软件&#xff0c;Photoshop 为设计者提供了许多…