Semantyka obsługi błędów podyktuj to:
Jest to realizowane za pomocą podtransakcji, które są zasadniczo takie same jak punkty zapisu . Innymi słowy, gdy uruchomisz następujący kod PL/pgSQL:
BEGIN
PERFORM foo();
EXCEPTION WHEN others THEN
PERFORM handle_error();
END
...to, co się właściwie dzieje, wygląda mniej więcej tak:
BEGIN
SAVEPOINT a;
PERFORM foo();
RELEASE SAVEPOINT a;
EXCEPTION WHEN others THEN
ROLLBACK TO SAVEPOINT a;
PERFORM handle_error();
END
COMMIT
wewnątrz bloku złamałoby to całkowicie; Twoje zmiany zostaną wprowadzone na stałe, punkt zapisu zostanie odrzucony, a program obsługi wyjątków pozostanie bez możliwości przywrócenia. W rezultacie zatwierdzenia nie są dozwolone w tym kontekście i próba wykonania COMMIT
spowoduje błąd „nie można zatwierdzić, gdy podtransakcja jest aktywna”.
Dlatego widzisz, że twoja procedura przeskakuje do procedury obsługi wyjątków zamiast uruchamiać raise notice 'B'
:kiedy dotrze do commit
, zgłasza błąd, a program obsługi go przechwytuje.
Jest to jednak dość proste do obejścia. BEGIN ... END
bloki mogą być zagnieżdżane i tylko bloki z EXCEPTION
Klauzule obejmują ustawianie punktów zapisu, więc możesz po prostu opakować polecenia przed i po zatwierdzeniu w ich własne programy obsługi wyjątków:
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;
$$;
Niestety prowadzi to do wielu duplikatów w procedurach obsługi błędów, ale nie mogę wymyślić fajnego sposobu, aby tego uniknąć.