40.8. Управление транзакциями#
40.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
).
Типичные шаблоны использования точек сохранения могут быть заменены блоками с
обработчиками исключений (см. Раздел 40.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
).