只要任何SQL语句因错误而终止,Postgres就会自动中止事务,其中包括任何约束违规。例如:
glyph=# create table foo (bar integer, constraint blug check(bar > 5));
CREATE TABLE
glyph=# begin;
BEGIN
glyph=# insert into foo values (10);
INSERT 0 1
glyph=# insert into foo values (1);
ERROR: new row for relation "foo" violates check constraint "blug"
STATEMENT: insert into foo values (1);
ERROR: new row for relation "foo" violates check constraint "blug"
尚未发出任何消息,但该事务已回滚。我个人最喜欢的会议内容如下:
glyph=# commit;
ROLLBACK
...因为“ROLLBACK
”似乎是COMMIT
的奇怪的成功 - 消息。但实际上,它已被回滚,表中没有行:
glyph=# select * from foo;
bar
-----
(0 rows)
我知道我可以创建大量的SAVEPOINT
并以这种方式处理SQL中的错误,但这会增加数据库的流量,延迟更多(我可能必须处理{{1}的错误毕竟),效益相对较小。我真的只想用我的应用程序语言(Python)处理SAVEPOINT
的错误,所以我想从SQL中获取的唯一行为是错误不会触发自动回滚。我该怎么办?
答案 0 :(得分:1)
我非常对PostgreSQL来说是新手,但是PostgreSQL文档中针对触发器/服务器端编程的一个例子看起来就像它正是你想要的那样。
请参阅:http://www.postgresql.org/docs/9.2/static/trigger-example.html
来自页面的片段:“因此触发器充当非空约束,但不会中止事务。”
答案 1 :(得分:0)
我强烈建议SqlAlchemy并使用子交易。您可以编写如下代码:
#some code
Session.begin(subtransactions=True)
#more stuff including sql activity then:
with Session.begin(nested=True):
# get the security
try:
foo = MyCodeToMakeFOO(args)
Session.add(foo)
Session.flush()
except:
log.error("Database hated your foo(%s) but I'll do the rest"%foo)
当子事务处于循环中时,最有用的是您要处理好记录并记录坏记录。
答案 2 :(得分:0)
我知道这是一张非常旧的机票,但是(截至2017年)PostgreSQL在提交中出现问题时仍然会自动回滚自身。我想在这里分享一些想法。
我不知道我们是否可以改变这种行为,而且我不需要这样做,也许是为了最好地委托PostgreSQL为我们管理回滚(他知道他在做什么,对吧? )。回滚意味着在失败的事务之前将数据更改回其原始状态,这意味着从触发器中更改或插入的数据也将被丢弃。在ACID逻辑中,这就是我们想要的。假设您自己管理后端的回滚,如果在自定义回滚期间出现问题,或者在回滚期间数据库同时从外部事务中更改,则数据会变得不一致,并且您的整个结构最有可能崩溃。
因此,知道PostgreSQL将管理自己的回滚策略,要问的问题是"如何扩展回滚策略?" 。你首先应该想到的是"是什么导致交易失败?" 。在 try / catch 结构中,尝试处理所有可能的异常并再次运行事务,或者通过一些适当的"不要做&#34发送反馈到前端应用程序;消息。对我来说,这是处理事物的最佳方式,代码更少,开销更少,控制更多,用户友好,数据库也会感谢你。
我想说明的最后一点,SQL标准有一个 sqlstate代码,可用于与后端模块进行通信。 事务期间失败的操作将返回sqlstate代码,然后您可以使用这些代码来产生适当的缺陷。您可以创建自己的sqlstate代码,只要它不会混淆保留的代码(https://www.postgresql.org/message-id/20185.1359219138%40sss.pgh.pa.us)。 例如,在plpgsql函数中
...
$$
begin
...do something...if it goes wrong
raise exception 'custom exception message' using errcode='12345';
end
$$
...
这是在PHP中使用PDO的示例(使用上面的错误代码):
...
$pdo->beginTransaction();
try {
$s = $pdo->prepare('...');
$s->execute([$value]);
/**
* Simulate a null violation exception
* If it fails, PDO will not wait the commit
* and will throw the exception, the code below
* is not executed.
*/
$s->execute([null]);
/**
* If nothing wrong happened, we commit to change
* the database state.
*/
$pdo->commit();
}
catch (PDOException $e) {
/**
* It is important to also have the commit here.
* It will trigger PostgreSQL rollback.
* And make the pdo Object back to auto-commit mode.
*/
$pdo->commit();
if ($e->getCode() === '12345') {
send_feedback_to_client('please do not hack us.');
}
}
...