PostgreSQL 中文参考文档
🌐 英文原版 官方 ↗

41.8. 事务管理 #

在由 CALL 命令调用的过程中, 以及在匿名代码块(DO 命令)中, 可以使用 COMMIT 和 ROLLBACK 命令结束事务。使用这些命令结束一个事务之后, 会自动开始一个新事务,因此不需要单独的 START TRANSACTION 命令。 (注意,BEGIN 和 END 在 PL/pgSQL 中有着不同的含义。)

下面是一个简单的示例:

CREATE PROCEDURE transaction_test1()
LANGUAGE plpgsql
AS $$
BEGIN
    FOR i IN 0..9 LOOP
        INSERT INTO test1 (a) VALUES (i);
        IF i % 2 = 0 THEN
            COMMIT;
        ELSE
            ROLLBACK;
        END IF;
    END LOOP;
END;
$$;

CALL transaction_test1();

一个新事务以默认的事务特征开始, 例如事务隔离级别。 当事务在循环中被提交时, 可能希望新事务自动以前一个事务相同的特征开始。 命令 COMMIT AND CHAIN 和 ROLLBACK AND CHAIN 可以做到这一点。

只有当 CALL 或 DO 调用 来自顶层,或者彼此嵌套且中间没有插入任何其他命令时, 才能在其中进行事务控制。例如,如果调用栈是 CALL proc1() → CALL proc2() → CALL proc3(),那么第二个和第三个过程 可以执行事务控制动作。但如果调用栈是 CALL proc1() → SELECT func2() → CALL proc3(), 那么最后一个过程就不能做事务控制了, 因为中间隔着一个 SELECT。

PL/pgSQL 不支持保存点 (SAVEPOINT/ROLLBACK TO SAVEPOINT/RELEASE SAVEPOINT 命令)。 保存点的典型使用模式可以由带异常处理程序的块替代 (见 Section 41.6.8)。 在底层,带异常处理程序的块会形成一个子事务, 这意味着事务不能在这样的块内部被结束。

对于游标循环,有一些需要特殊考虑的事项。考虑这个示例:

CREATE PROCEDURE transaction_test2()
LANGUAGE plpgsql
AS $$
DECLARE
    r RECORD;
BEGIN
    FOR r IN SELECT * FROM test2 ORDER BY x LOOP
        INSERT INTO test1 (a) VALUES (r.x);
        COMMIT;
    END LOOP;
END;
$$;

CALL transaction_test2();

正常情况下,游标会在事务提交时被自动关闭。 然而,作为这种循环的一部分创建的游标会在第一次 COMMIT 或 ROLLBACK 时被自动转换为可保持(holdable)游标。 这意味着游标会在第一次 COMMIT 或 ROLLBACK 时被完整求值, 而不是逐行求值。游标仍然会在循环结束后被自动移除, 所以这一点对用户来说基本不可见。 但必须记住,游标查询所获取的任何表锁或行锁 在第一次 COMMIT 或 ROLLBACK 之后就不会再被保持了。

在由非只读命令(例如 UPDATE ... RETURNING)驱动的游标循环中, 不允许使用事务命令。