在由 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)驱动的游标循环中,
不允许使用事务命令。