Semantikken i PL/pgSQL's fejlhåndtering diktere at:
Dette er implementeret ved hjælp af undertransaktioner, som grundlæggende er det samme som savepoints . Med andre ord, når du kører følgende PL/pgSQL-kode:
BEGIN
PERFORM foo();
EXCEPTION WHEN others THEN
PERFORM handle_error();
END
...hvad der rent faktisk sker er sådan noget her:
BEGIN
SAVEPOINT a;
PERFORM foo();
RELEASE SAVEPOINT a;
EXCEPTION WHEN others THEN
ROLLBACK TO SAVEPOINT a;
PERFORM handle_error();
END
En COMMIT
inden for blokken ville bryde dette fuldstændigt; dine ændringer ville blive gjort permanente, savepunktet ville blive kasseret, og undtagelsesbehandleren ville ikke kunne rulle tilbage. Som et resultat er commits ikke tilladt i denne sammenhæng, og forsøg på at udføre en COMMIT
vil resultere i fejlen "kan ikke foretage, mens en undertransaktion er aktiv".
Det er derfor, du ser din procedure springe til undtagelsesbehandleren i stedet for at køre raise notice 'B'
:når den når commit
, det kaster en fejl, og handleren fanger den.
Dette er dog ret ligetil at omgås. BEGIN ... END
blokke kan indlejres, og kun blokke med EXCEPTION
klausuler involverer indstilling af savepoints, så du bare kan pakke kommandoerne før og efter commit i deres egne undtagelsesbehandlere:
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;
$$;
Desværre fører det til en masse dobbeltarbejde i fejlbehandlerne, men jeg kan ikke komme i tanke om en god måde at undgå det på.