海山数据库(He3DB)源码详解:RollbackToSavepoint函数
1. 执行条件
执行ROLLBACK TO <savepoint>命令时,底层调用RollbackToSavepoint函数执行子事务回滚。
2. 执行流程
当然可以,下面是一个简化的 RollbackToSavepoint
语句的执行流程的框架图:
这个流程图描述了从开始执行 RollbackToSavepoint
命令到最终完成或发生错误的整个过程。每个步骤都是决策点或操作,其中:
- B 代表解析
ROLLBACK TO SAVEPOINT
命令。 - C 是一个决策点,检查保存点是否存在。
- D 到 J 描述了正常的回滚流程。
- K 是另一个决策点,检查在回滚过程中是否发生错误。
- L 表示如果发生错误,则进行完全回滚。
- M 表示成功返回回滚状态给客户端。
- N 表示如果发生错误,则通知客户端。
- O 表示操作完成。
请注意,这个流程图是一个高层次的抽象,实际的 PostgreSQL 源码实现会更加复杂,并且涉及到许多底层的函数和数据结构,因此本文会在下一节详细介绍源码的执行结果。
3. 源码解读
1、设定事务状态
获取当前事务状态,创建目标父节点和临时事务状态变量。
TransactionState s = CurrentTransactionState;
TransactionState target, xact;
if (IsInParallelMode())
ereport(ERROR,
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
errmsg("cannot rollback to savepoints during a parallel
operation")));
- 通过全局变量CurrentTransactionState获得当前事务状态,创建目标父节点事务状态变量target和临时事务状态变量xact;
- 检查当前事务是否为并行事务,是则报错退出。
2、检查事务块状态
通过switch-case结构检查当前事务块的状态。
/* We can't rollback to a savepoint if there is no savepoint defined.*/
case TBLOCK_INPROGRESS:
case TBLOCK_ABORT:
ereport(ERROR,
(errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
errmsg("savepoint \"%s\" does not exist", name)));
break;
case TBLOCK_IMPLICIT_INPROGRESS:
ereport(ERROR,
(errcode(ERRCODE_NO_ACTIVE_SQL_TRANSACTION),
errmsg("%s can only be used in transaction blocks",
"ROLLBACK TO SAVEPOINT")));
break;
/* There is at least one savepoint, so proceed. */
case TBLOCK_SUBINPROGRESS:
case TBLOCK_SUBABORT:
break;
/* These cases are invalid. */
case TBLOCK_DEFAULT:
/* ... */
elog(FATAL, "RollbackToSavepoint: unexpected state %s",
BlockStateAsString(s->blockState));
break;
- 事务处于TBLOCK_SUBINPROGRESS或TBLOCK_SUBABORT状态:正确状态,跳出switch-case结构;
- 事务处于TBLOCK_INPROGRESS或TBLOCK_ABORT状态:错误的状态,两种状态下不存在SAVEPOINT,无法执行子事务回滚;
- 事务处于TBLOCK_IMPLICIT_INPROGRESS状态:无效的状态,隐式事务状态下无法执行子事务回归状态,不能执行ROLLBACK TO <savepoint> 命令;
- 事务处于除上述之外的其他状态:无效的状态,提交FATAL日志。
3、回溯寻找目标节点
通过for循环回溯链栈,寻找目标节点target。
for (target = s; PointerIsValid(target); target = target->parent)
{
if (PointerIsValid(target->name) && strcmp(target->name, name) == 0)
break;
}
- PointerIsValid本质为宏定义,作用为判断参数是否为NULL;
- 判断target->name是否为空,且是否等于目标节点的name。如果为True,则跳出for循环;
4、检查目标节点
检查目标节点target的状态,同时检查保存点等级。
if (!PointerIsValid(target))
ereport(ERROR,
(errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
errmsg("savepoint \"%s\" does not exist", name)));
/* disallow crossing savepoint level boundaries */
if (target->savepointLevel != s->savepointLevel)
ereport(ERROR,
(errcode(ERRCODE_S_E_INVALID_SPECIFICATION),
errmsg("savepoint \"%s\" does not exist within current
savepoint level", name)));
- 跳出循环后,检查目标节点target是否有效;
- 检查目标节点的保存点等级和当前子事务点的保存点等级是否一致。
5、修改目标点之前的状态
将当前事务到目标节点之间的状态标记为ABORT PENDING或ABORT END状态。
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));
}
- 将全局状态变量CurrentTransactionState赋值给临时事务状态变量xact;
- 死循环中,检查临时变量xact是否为target节点,相同则跳出死循环;
- 如果临时变量xact的blockState为TBLOCK_SUBINPROGRESS,将事务块状态修改为TBLOCK_SUBABORT_PENDING;
- 如果临时变量xact的blockState为TBLOCK_SUBABORT,将事务块状态修改为TBLOCK_SUBABORT_END;
- 如果临时变量xact的事务块状态为其他状态,则提交FATAL日志;
- 将xact修改为父节点事务状态,判断修改后的事务状态是否有效,继续死循环。
6、修改目标点的状态
修改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;
else
elog(FATAL, "RollbackToSavepoint: unexpected state %s",
BlockStateAsString(xact->blockState));
- 如果临时变量xact的blockState为TBLOCK_SUBINPROGRESS,将事务块状态修改为TBLOCK_SUBRESTART;
- 如果临时变量xact的blockState为TBLOCK_SUBABORT,将事务块状态修改为TBLOCK_SUBABORT_RESTART;
- 如果临时变量xact的事务块状态为其他状态,则提交FATAL日志;
完成RollbackToSavepoint函数。
作者介绍
李超,移动云数据库工程师,负责云原生数据库He3DB的研发。
RT,将事务块状态修改为TBLOCK_SUBABORT_RESTART;
3. 如果临时变量xact的事务块状态为其他状态,则提交FATAL日志;
完成RollbackToSavepoint函数。
作者介绍
李超,移动云数据库工程师,负责云原生数据库He3DB的研发。