Семантика обработки ошибок PL / pgSQL диктует, что:
Когда ошибка обнаружена предложением EXCEPTION ... все изменения в постоянном состоянии базы данных в блокеоткатывается.
Это реализовано с использованием субтранзакций, которые в основном совпадают с точками сохранения .Другими словами, когда вы запускаете следующий код PL / pgSQL:
BEGIN
PERFORM foo();
EXCEPTION WHEN others THEN
PERFORM handle_error();
END
... на самом деле происходит что-то вроде этого:
BEGIN
SAVEPOINT a;
PERFORM foo();
RELEASE SAVEPOINT a;
EXCEPTION WHEN others THEN
ROLLBACK TO SAVEPOINT a;
PERFORM handle_error();
END
A COMMIT
внутри блокасломал бы это полностью;Ваши изменения будут сделаны постоянными, точка сохранения будет отброшена, а обработчик исключений останется без возможности отката.В результате коммиты в этом контексте не допускаются, и попытка выполнить COMMIT
приведет к ошибке «невозможно зафиксировать, пока субтранзакция активна».
Вот почему вы видите, что ваша процедура переходит к обработчику исключений вместо запуска raise notice 'B'
: когда он достигает commit
, он выдает ошибку, и обработчик ловит ее.
Это довольно просто обойти, хотя.Блоки BEGIN ... END
могут быть вложенными, и только блоки с предложениями EXCEPTION
включают установку точек сохранения, поэтому вы можете просто обернуть команды до и после фиксации в их собственных обработчиках исключений:
create or replace procedure x_transaction_try() language plpgsql
as $$
declare
my_ex_state text;
my_ex_message text;
my_ex_detail text;
my_ex_hint text;
my_ex_ctx text;
begin
begin
raise notice 'A';
exception when others then
raise notice 'C';
GET STACKED DIAGNOSTICS
my_ex_state = RETURNED_SQLSTATE,
my_ex_message = MESSAGE_TEXT,
my_ex_detail = PG_EXCEPTION_DETAIL,
my_ex_hint = PG_EXCEPTION_HINT,
my_ex_ctx = PG_EXCEPTION_CONTEXT
;
raise notice '% % % % %', my_ex_state, my_ex_message, my_ex_detail, my_ex_hint, my_ex_ctx;
end;
commit;
begin
raise notice 'B';
exception when others then
raise notice 'C';
GET STACKED DIAGNOSTICS
my_ex_state = RETURNED_SQLSTATE,
my_ex_message = MESSAGE_TEXT,
my_ex_detail = PG_EXCEPTION_DETAIL,
my_ex_hint = PG_EXCEPTION_HINT,
my_ex_ctx = PG_EXCEPTION_CONTEXT
;
raise notice '% % % % %', my_ex_state, my_ex_message, my_ex_detail, my_ex_hint, my_ex_ctx;
end;
end;
$$;
К сожалению, этодействительно приводит к большому дублированию в обработчиках ошибок, но я не могу придумать хороший способ избежать этого.