40.8. Управление транзакциями#

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).