Semantiken för PL/pgSQL:s felhantering diktera att:
Detta implementeras med hjälp av undertransaktioner, som i princip är samma som savepoints . Med andra ord, när du kör följande PL/pgSQL-kod:
BEGIN
PERFORM foo();
EXCEPTION WHEN others THEN
PERFORM handle_error();
END
...det som faktiskt händer är ungefär så här:
BEGIN
SAVEPOINT a;
PERFORM foo();
RELEASE SAVEPOINT a;
EXCEPTION WHEN others THEN
ROLLBACK TO SAVEPOINT a;
PERFORM handle_error();
END
En COMMIT
inom blocket skulle bryta detta helt; dina ändringar skulle göras permanenta, räddningspunkten skulle förkastas och undantagshanteraren skulle inte kunna rulla tillbaka. Som ett resultat är commits inte tillåtna i det här sammanhanget, och försök att köra en COMMIT
kommer att resultera i felet "kan inte begå medan en deltransaktion är aktiv".
Det är därför du ser din procedur hoppa till undantagshanteraren istället för att köra raise notice 'B'
:när den når commit
, det ger ett fel och hanteraren fångar det.
Detta är dock ganska enkelt att komma runt. BEGIN ... END
block kan kapslas, och endast block med EXCEPTION
satser involverar inställning av räddningspunkter, så du kan bara linda kommandona före och efter commit i deras egna undantagshanterare:
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;
$$;
Tyvärr leder det till en hel del dubbelarbete i felhanterarna, men jag kan inte komma på något bra sätt att undvika det.