【发布时间】:2015-01-11 11:01:41
【问题描述】:
我正在编写一些使用 libpq 与 PostgreSQL 数据库交互的 C++ 代码,并且我已经开始编写一些函数,每个函数在内部启动一个事务,对数据库应用一些更改,然后结束交易。
我现在想结合另一个 DML 语句调用这些函数之一,所有这些都在同一个事务中执行。像这样的东西(注意:非常简化):
void doFoo(PGconn* con) {
PQexec(con, "BEGIN" );
PQexec(con, "insert into ..." );
PQexec(con, "delete from ..." );
PQexec(con, "END" );
}
void doFooPlus(PGconn* con) {
PQexec(con, "BEGIN" );
doFoo(con);
PQexec(con, "update ..." );
PQexec(con, "END" );
}
void main(void) {
doFooPlus(con);
}
但是,根据我所阅读的所有内容,PostgreSQL 似乎可能不支持这种事务嵌套。我想明确一点:我不需要自主事务,我知道 PostgreSQL 不支持,并且我不需要任何显式回滚功能到嵌套(或其他)BEGIN 语句,这可以通过保存点来完成,并且显然不是上面的代码在任何时候都试图做的。我只是想从代码的结构上确认上面的代码是否会做人们希望它会做的事情。
让我尝试进一步澄清。以下是 PostgreSQL 最终会从上面的 C++(实际上只是直接的 C)代码中看到的内容:
BEGIN
BEGIN
insert into ...
delete from ...
END
update ...
END
我担心的是第二个 BEGIN 调用被完全忽略,因此第一个 END 调用将结束由第一个 BEGIN 调用启动的事务,因此更新语句将不包含在事务块的原子性中。
根据http://www.postgresql.org/docs/9.4/static/sql-begin.html:
当已经在事务块中时发出 BEGIN 将引发警告消息。事务的状态不受影响。要将事务嵌套在事务块中,请使用保存点(请参阅 SAVEPOINT)。
关于保存点的评论对我来说似乎有点误导; PostgreSQL 不支持嵌套(自治)事务;保存点仅提供一种回滚到现有事务中间点的方法。保存点本身不是事务,所以我不能用它们替换 doFoo() 中的 BEGIN 和 END,因为那样我就不能自己调用 doFoo()(意味着不是来自 doFooPlus())并且仍然获得事务原子性doFoo()执行的插入和删除操作。
关于嵌套 BEGIN 的事务状态“不[被]影响”的评论似乎暗示 PostgreSQL 不会计算它,实际上会完全忽略它,但引用的短语并没有 相当让我清楚地知道我不会在 Stack Overflow 上问这个问题。我仍然抱有一丝希望,PostgreSQL 仍然会将嵌套的 BEGIN 计入某种内部“嵌套级别”,这将由第一个 END 递减,然后由第二个 END 再次递减,导致整个序列语句被视为一个原子事务。
那么,有人可以确认 PostgreSQL 是否这样做吗?如果没有,您能否提供有关如何在我的代码中最好地解决此问题的建议?我正在考虑添加一个 bool 参数,以允许 doFoo() 的调用者指定是否创建事务,doFooPlus() 可以将其传递给 false。
编辑:对于任何有兴趣的人,我今天意识到我可以很容易地自己测试这个问题,只需编写一个程序来执行上面示例代码试图做的事情,并且然后检查它对数据库的影响。
关于程序的内部我就不过多赘述了,但是下面的命令基本上是运行create table t1 (a int, b int, c int ); insert into t1 (a,b,c) values (0,0,0);,然后依次运行给定的每个SQL语句,最后打印出结果表数据,所以你可以看到第二个begin和最后一个rollback被完全忽略了:
> pgtestabc begin begin 'update t1 set a=1;' 'update t1 set b=1;' end 'update t1 set c=1;' rollback;
executing "begin"...done
executing "begin"...done
executing "update t1 set a=1;"...done
executing "update t1 set b=1;"...done
executing "end"...done
executing "update t1 set c=1;"...done
executing "rollback"...done
1|1|1
还请注意,您无法通过简单地从 pgAdmin III 等 GUI 客户端批量运行 SQL 语句来进行精确测试。那个特定的客户似乎在交易方面做了一些魔术;它似乎将批处理包含在隐式事务中,因此rollback 语句将导致先前的语句回滚(即使您还在“消息”窗格中收到“注意:没有正在进行的事务”消息。 ..),除了它仍然以某种方式尊重批处理中的begin...end 块(忽略上面演示的嵌套begin 语句),这令人困惑地似乎与postgres 所做的自治事务非常相似不支持,因为我相信我们已经在这个线程中建立了。因此,例如,如果您直接在 pgAdmin III 中运行上述 7 条语句,您最终会得到的数据是 1|1|0。
但不管这种无意的混淆,无可争辩的结论是 postgres 确实不计算 begin...end 块的嵌套级别,所以你必须小心只放置自己在一个顶级 begin...end 块内。
【问题讨论】:
-
Postgresql 支持自治子事务 (AST) wiki.postgresql.org/wiki/Autonomous_subtransactions
-
感谢您提供的信息,我不了解 AST。但是,这对我没有帮助,因为我希望能够调用
doFoo()并获得正常(非子)事务,但我也希望能够调用doFooPlus()并获得整个语句序列,包括doFoo()'s,在原子事务中。这会像我在问题中编写示例代码的方式那样发生吗? -
@Jérôme:该页面将自治子事务描述为一个一般概念,但没有提到 Postgres 实际实现了它们(我很确定它没有)。
-
我同意@NickBarnes 的观点,这只是一个讨论,是未来最终实施的指导方针。我将 postgresql 与 c 一起使用(用于泥浆游戏)。我曾经编写 postgresql 函数并从 c 中调用它们。如果你真的需要处理更多 c++ 函数之间的事务是的,最好是知道状态 ok 事务。作为 DBA,我不喜欢客户端驱动的事务。我认为将它们放在 pgsql 函数中更安全。
标签: c++ postgresql transactions libpq