我遇到过这样的情况:我必须将一部分代码作为其自己的事务提交。
我创建了一个表
subtransaction_tbl
:
CREATE TABLE subtransaction_tbl
(
entryval integer
)
以及 plpython3u 语言中的函数:
CREATE FUNCTION subtransaction_nested_test_t() RETURNS void
AS $$
plpy.execute("INSERT INTO subtransaction_tbl VALUES (1)")
with plpy.subtransaction():
plpy.execute("INSERT INTO subtransaction_tbl VALUES (2)")
$$ LANGUAGE plpython3u;
第一种情况:
BEGIN TRANSACTION;
INSERT INTO subtransaction_tbl VALUES (4);
select subtransaction_nested_test_t();
COMMIT TRANSACTION;
表格中的条目正确:1,2,4
第二种情况:
BEGIN TRANSACTION;
INSERT INTO subtransaction_tbl VALUES (4);
select subtransaction_nested_test_t();
ROLLBACK TRANSACTION;
表中的值未填充
我预计
1
或 2
应添加到表 subtransaction_tbl
但令我惊讶的是没有插入任何值。我想象该函数打开了一个新的子事务,它不应该依赖于父事务。请告诉我我的说法是否正确。
Postgres 中有自治事务吗?或者我必须修改我的 plpython3u 函数吗?
Postgres确实支持嵌套事务,但它们与传统的SQL不同,更像是带有嵌套部分点的事务。
在顶层,您始终具有典型的
BEGIN/COMMIT/ROLLBACK
,在嵌套级别上,您必须使用以下命令:
SAVEPOINT name
- 创建一个新的保存点,其名称对于事务来说是唯一的RELEASE SAVEPOINT name
- 提交保存点,但只有包含事务提交时它才会持续存在ROLLBACK TO SAVEPOINT name
- 回滚保存点您还必须确保:
SAVEPOINT
使用的名称都是唯一的;SAVEPOINT
的失败会向上传播到顶层。最后一点有点棘手,除非您使用可以自动为您完成此操作的库。
当我写下pg-promise时,我确保这两个条款得到保证:
sp_xy
,其中x
是当前任务/事务深度,y
是实际事务级别+1;ROLLBACK TO SAVEPOINT name
,加上顶级 ROLLBACK
,以防子事务失败 - 所有这些都建立在标准的承诺链逻辑上。另请参阅 PostgreSQL 嵌套事务的限制解释...
在 Postgres 11 之前,Postgres 中没有自治事务,其中添加了 SQL 过程。参见:
函数中完成的所有操作都随事务一起提交或回滚。
在 Postgres 10 或更早版本中,解决方法可能是(ab-)使用 dblink:
SAVEPOINT
的相关概念。 (不是同一件事!):
plpython 有 子事务 (
with plpy.subtransaction():
),但这与自治事务不同。没有单独的COMMIT
。它所做的就是将几个语句捆绑在一起,使它们成为原子的。如果没有这个,如果中间某个地方发生异常,并且您捕获该异常,则只会执行该异常之前的代码。如果将其包装到子事务中,则要么全有,要么全无。这就像使用 SAVEPOINT
,而不是自主交易。 手册:
子事务上下文管理器不会捕获错误,它只会捕获错误 确保在其范围内执行的所有数据库操作都将 原子提交或回滚。
以下是对该功能的讨论:
@SarthAk 这是一个支持自主交易(WIP)的补丁。如果你愿意,你可以修补 Postgres master 源代码并编译
https://www.postgresql.org/message-id/flat/f7470d5a-3cf1-4919-8404-5c4d91341a9f%40tantorlabs.com
FOSSAsia 的视频及说明: