5

我正在使用pg-promise来运行我的 SQL 查询。查询本身存储在外部 .sql 文件中。

当我执行事务时,如果发生错误(如预期的那样),Postgres 将中止事务。我遇到的问题是我在事务中止后尝试运行的任何单独查询都没有运行,而是收到以下消息: "current transaction is aborted, commands ignored until end of transaction block"。如果查询是在 psql 控制台中运行的,我可以通过在查询失败后发出 ROLLBACK 来解决这个问题。我不认为这是一个选项,因为我的应用程序使用的 SQL 位于外部文件中。我也不认为保存点是一种选择,因为如果出现故障,整个事务都应该被丢弃。

如果发生此错误,我将如何回滚 SQL 文件?

这是供参考的SQL:

BEGIN;

DELETE 
FROM tournament_tossup_values
WHERE tournament_id = $1 AND
NOT EXISTS
(
    SELECT id
    FROM tournament_match
    WHERE tournament_id = $1
);

UPDATE tournament
SET bonus_point_value = $5, parts_per_bonus = $6
WHERE id = $1 AND NOT EXISTS (
    SELECT id 
    FROM tournament_match
    WHERE tournament_id = $1
)
RETURNING bonus_point_value, parts_per_bonus; <-- Any subsequent accesses to the database by the application fail if this transaction fails

COMMIT;  <-- I want to rollback everything if this fails

先感谢您!

4

1 回答 1

5

在外部 SQL 文件中实现事务时,您需要为COMMIT和提供所有适当的处理ROLLBACK。如果您不这样做,则事务状态可能会在您的服务器端代码中变得不可预测,并导致您遇到的错误类型。

这可能有点棘手,说起来容易做起来难。这就是为什么最好的解决方案是根本不这样做。

您已经在使用的模块pg-promise通过方法tx为事务提供可靠的处理,这是您应该使用的。

tx方法文档:

将回调函数作为事务执行 (...)

事务将常规任务包装到附加查询中:

BEGIN在调用回调函数之前执行

它执行COMMIT,如果回调没有抛出任何错误或返回被拒绝的承诺,它执行ROLLBACK,如果回调确实抛出错误或返回被拒绝的承诺

SAVEPOINT当方法被递归调用时,它会执行相应 的命令。

为此,将您的 SQL 文件拆分为两个文件 - 一个包含您的DELETE操作,一个包含您的UPDATE操作,然后在事务中将它们作为两个查询执行:

await db.tx(async t => {
    await t.none('DELETE...');
    await t.any('UPDATE...');
});
于 2016-07-25T02:14:45.050 回答