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 или DO без других промежуточных команд. Например, в стеке вызовов CALL proc1()CALL proc2()CALL proc3() вторая и третья процедуры могут управлять транзакциями. Но в стеке CALL proc1()SELECT func2()CALL proc3() последняя процедура лишена этой возможности из-за промежуточного SELECT.

PL/pgSQL не поддерживает точки сохранения (команды SAVEPOINT/ROLLBACK TO SAVEPOINT/RELEASE SAVEPOINT). Типичные схемы использования точек сохранения можно реализовать при помощи блоков с обработчиками исключений (см. Подраздел 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. Это означает, что курсор полностью вычисляется при выполнении первой команды COMMIT или ROLLBACK, а не для каждой очередной строки. При этом он автоматически удаляется после цикла, так что это происходит практически незаметно для пользователя. Однако стоит учитывать, что любые блокировки таблиц или строк, принимаемые запросом курсора, не будут удерживаться после первого выполнения команд COMMIT или ROLLBACK.

Команды управления транзакциями не допускаются в циклах с курсором, которыми управляют запросы, производящие не только чтение, но и модификацию данных (например, UPDATE ... RETURNING).