La semantica della gestione degli errori dettalo:
Questo viene implementato utilizzando sottotransazioni, che sono sostanzialmente le stesse di savepoints . In altre parole, quando esegui il seguente codice PL/pgSQL:
BEGIN
PERFORM foo();
EXCEPTION WHEN others THEN
PERFORM handle_error();
END
...quello che sta effettivamente accadendo è qualcosa del genere:
BEGIN
SAVEPOINT a;
PERFORM foo();
RELEASE SAVEPOINT a;
EXCEPTION WHEN others THEN
ROLLBACK TO SAVEPOINT a;
PERFORM handle_error();
END
Un COMMIT
all'interno del blocco lo spezzerebbe completamente; le tue modifiche verrebbero rese permanenti, il punto di salvataggio verrebbe scartato e il gestore delle eccezioni verrebbe lasciato senza modo per tornare indietro. Di conseguenza, i commit non sono consentiti in questo contesto e il tentativo di eseguire un COMMIT
risulterà in un errore "impossibile eseguire il commit mentre una sottotransazione è attiva".
Ecco perché vedi la tua procedura passare al gestore delle eccezioni invece di eseguire raise notice 'B'
:quando raggiunge il commit
, genera un errore e il gestore lo rileva.
Questo è abbastanza semplice da aggirare, però. BEGIN ... END
i blocchi possono essere nidificati e solo i blocchi con EXCEPTION
le clausole implicano l'impostazione di punti di salvataggio, quindi puoi semplicemente racchiudere i comandi prima e dopo il commit nei loro gestori di eccezioni:
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;
$$;
Sfortunatamente, porta a molte duplicazioni nei gestori degli errori, ma non riesco a pensare a un modo carino per evitarlo.