目录结构
注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下:
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;
...
接下来就是真正执行SELECT
和 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;
至此INFO5结束!
注:这里我们先后执行了SELECT
和INSERT
,他们在具体的处理上有一处不同,如下:
前者的为假;后者为真。关于这个问题,后面我将另起一篇博客详细说明 这里不再赘述!
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 结束!!!