43.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
или DO
без других промежуточных команд. Например, в стеке вызовов CALL proc1()
→ CALL proc2()
→ CALL proc3()
вторая и третья процедуры могут управлять транзакциями. Но в стеке CALL proc1()
→ SELECT func2()
→ CALL proc3()
последняя процедура лишена этой возможности из-за промежуточного SELECT
.
PL/pgSQL не поддерживает точки сохранения (команды SAVEPOINT
/ROLLBACK TO SAVEPOINT
/RELEASE SAVEPOINT
). Типичные схемы использования точек сохранения можно реализовать при помощи блоков с обработчиками исключений (см. Подраздел 43.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
. Это означает, что курсор полностью вычисляется при выполнении первой команды COMMIT
или ROLLBACK
, а не для каждой очередной строки. При этом он автоматически удаляется после цикла, так что это происходит практически незаметно для пользователя. Однако стоит учитывать, что любые блокировки таблиц или строк, принимаемые запросом курсора, не будут удерживаться после первого выполнения команд COMMIT
или ROLLBACK
.
Команды управления транзакциями не допускаются в циклах с курсором, которыми управляют запросы, производящие не только чтение, но и модификацию данных (например, UPDATE ... RETURNING
).