PostgreSQL Error 2D522 invalid_transaction_termination: Разбор ошибки
- Ошибка выполнения SQL:
ERROR: 2D522: invalid transaction termination. - Сбой выполнения функций PL/pgSQL при попытке явного управления транзакциями (
COMMITилиROLLBACK). - Падение триггеров баз данных при попытке завершить транзакцию внутри тела триггерной функции.
- Отказ выполнения пакетов интеграций между 1С и сторонними сервисами через вызов RPC-функций PostgreSQL.
1. Архитектурное ограничение функций в PostgreSQL
В PostgreSQL функции (созданные через CREATE FUNCTION) всегда выполняются внутри транзакционного контекста вызывающего запроса и не имеют права выполнять COMMIT или ROLLBACK. Нарушение этого правила приводит к генерации исключения 2D522.
2. Миграция с CREATE FUNCTION на CREATE PROCEDURE (PostgreSQL 11+)
Начиная с PostgreSQL 11, для поддержки управления транзакциями внутри процедурного кода были добавлены хранимые процедуры (PROCEDURE):
-- ОШИБОЧНЫЙ ВАРИАНТ (вызовет 2D522):
CREATE OR REPLACE FUNCTION bad_batch_process()
RETURNS void AS $$
BEGIN
INSERT INTO logs VALUES ('Step 1');
COMMIT; -- ОШИБКА 2D522!
END;
$$ LANGUAGE plpgsql;
-- ПРАВИЛЬНЫЙ ВАРИАНТ (Использование PROCEDURE):
CREATE OR REPLACE PROCEDURE good_batch_process()
LANGUAGE plpgsql AS $$
BEGIN
INSERT INTO logs VALUES ('Step 1');
COMMIT; -- Полностью разрешено
INSERT INTO logs VALUES ('Step 2');
ROLLBACK; -- Полностью разрешено
END;
$$;
-- Вызов процедуры:
CALL good_batch_process();
3. Ограничения процедур внутри явных транзакций и функций
Даже хранимая процедура не может выполнить COMMIT, если она вызвана внутри внешнего блока BEGIN ... COMMIT клиента или из триггера:
-- Этот вызов упадет с 2D522, если внутри процедуры есть COMMIT:
BEGIN;
CALL good_batch_process(); -- Ошибка: нельзя коммитить внутри внешней транзакции
COMMIT;
-- Корректный вызов процедур с автономными транзакциями:
CALL good_batch_process(); -- Вне явного внешнего блока BEGIN
4. Обработка исключений вместо ручного ROLLBACK
Если цель использования ROLLBACK в функции — перехват ошибок, замените его на блок EXCEPTION:
CREATE OR REPLACE FUNCTION safe_insert_data()
RETURNS boolean AS $$
BEGIN
INSERT INTO target_table (id, data) VALUES (1, 'Test');
RETURN true;
EXCEPTION
WHEN unique_violation THEN
-- Транзакция откатится к неявной точке сохранения автоматически
RETURN false;
END;
$$ LANGUAGE plpgsql;
Частые вопросы (FAQ)
Можно ли выполнить COMMIT внутри триггера в PostgreSQL?
Нет. Триггеры в PostgreSQL всегда работают в контексте DML-операции (INSERT/UPDATE/DELETE), являющейся частью внешней транзакции. Управление транзакциями внутри триггеров принципиально запрещено.
В чем ключевая разница между FUNCTION и PROCEDURE относительно транзакций?
Функция (FUNCTION) возвращает значение и всегда атомарна (не может управлять транзакцией). Процедура (PROCEDURE) вызывается через CALL и может содержать операторы COMMIT и ROLLBACK для пакетной обработки данных.
Почему вызов процедуры из клиента 1С через ADODB/ODBC падает с 2D522?
Многие драйверы по умолчанию открывают неявную транзакцию перед каждым вызовом. Отключите авто-транзакции в свойствах коннектора (autocommit mode = on), чтобы разрешить процедуре управлять коммитами.
Поддерживает ли PostgreSQL автономные транзакции (Autonomous Transactions) как в Oracle?
Нативно нет. Для реализации автономных транзакций в функциях используется расширение dblink или pg_background, открывающее отдельную независимую сессию к серверу.