Mar*_*llo 9 postgresql transactions exception plpgsql rollback
我正在为 PostgreSQL 编写一个脚本,因为我希望它以原子方式执行,所以我将它包装在一个事务中。
我希望脚本看起来像这样:
BEGIN
-- 1) Execute some valid actions;
-- 2) Execute some action that causes an error.
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END; -- A.k.a. COMMIT;
Run Code Online (Sandbox Code Playgroud)
但是,在这种情况下,pgAdmin 在初始BEGIN. 如果我通过像这样附加分号来终止命令:BEGIN;它会通知我附近的错误EXCEPTION。
我意识到我可能混淆了控制结构和事务的语法,但是我在文档中找不到任何关于如何回滚失败事务的内容(也没有在 SO 中)。
我还考虑过事务可能会在出错时自动回滚,但由于以下脚本,情况似乎并非如此:
BEGIN;
-- 1) Execute some valid actions;
-- 2) Execute some action that causes an error.
COMMIT;
Run Code Online (Sandbox Code Playgroud)
警告我:ERROR: current transaction is aborted, commands ignored until end of transaction block然后我必须手动ROLLBACK;进行交易。
似乎我在这里遗漏了一些基本的东西,但是什么?
编辑:
我也试过DO像这样使用:
DO $$
BEGIN
-- 1) Execute some valid actions;
-- 2) Execute some action that causes an error.
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END; $$
Run Code Online (Sandbox Code Playgroud)
pgAdmin 回击我:ERROR: cannot begin/end transactions in PL/pgSQL. HINT: Use a BEGIN block with an EXCEPTION clause instead.这让我困惑不已,因为这正是我(我认为)正在做的事情。
接受后编辑: 关于 Laurenz 的评论:“您的 SQL 脚本将包含一个 COMMIT。这将结束事务并将其回滚。” - 这不是我观察到的行为。请考虑以下示例(这只是我在原始问题中已经提供的示例的具体版本):
BEGIN;
-- Just a simple, self-referencing table.
CREATE TABLE "Dummy" (
"Id" INT GENERATED ALWAYS AS IDENTITY,
"ParentId" INT NULL,
CONSTRAINT "PK_Dummy" PRIMARY KEY ("Id"),
CONSTRAINT "FK_Dummy_Dummy" FOREIGN KEY ("ParentId") REFERENCES "Dummy" ("Id")
);
-- Foreign key violation terminates the transaction.
INSERT INTO "Dummy" ("ParentId")
VALUES (99);
COMMIT;
Run Code Online (Sandbox Code Playgroud)
当我执行上面的脚本时,我得到了:ERROR: insert or update on table "Dummy" violates foreign key constraint "FK_Dummy_Dummy". DETAIL: Key (ParentId)=(99) is not present in table "Dummy".正如预期的那样。但是,如果我然后尝试检查我的Dummy表是否已创建或回滚,如下所示:
SELECT EXISTS (
SELECT FROM information_schema."tables"
WHERE "table_name" = 'Dummy');
Run Code Online (Sandbox Code Playgroud)
而不是一个简单的false,我得到了我已经提到过两次的相同错误:ERROR: current transaction is aborted, commands ignored until end of transaction block。然后我必须通过发布手动终止交易ROLLBACK;。
所以对我来说,上面提到的评论似乎是错误的,或者至少我在这里严重误解了一些东西。
您不能ROLLBACK在 PL/pgSQL 中使用,除非在过程内部的某些有限情况下。
您不需要在 PL/pgSQL 代码中显式回滚。只要让异常从 PL/pgSQL 代码中传播出去,就会导致错误,从而导致整个事务回滚。
您的评论表明此代码是从 SQL 脚本调用的。那么解决方案是COMMIT在 PL/pgSQL 代码之后的某个地方有一个SQL 脚本。这将结束事务并将其回滚。