Die Semantik der Fehlerbehandlung
Dies wird mithilfe von Untertransaktionen implementiert, die im Grunde dasselbe sind wie Speicherpunkte . Mit anderen Worten, wenn Sie den folgenden PL/pgSQL-Code ausführen:
BEGIN
PERFORM foo();
EXCEPTION WHEN others THEN
PERFORM handle_error();
END
...was tatsächlich passiert, sieht ungefähr so aus:
BEGIN
SAVEPOINT a;
PERFORM foo();
RELEASE SAVEPOINT a;
EXCEPTION WHEN others THEN
ROLLBACK TO SAVEPOINT a;
PERFORM handle_error();
END
Ein COMMIT
innerhalb des Blocks würde dies vollständig brechen; Ihre Änderungen würden dauerhaft gemacht, der Sicherungspunkt würde verworfen und der Ausnahmehandler hätte keine Möglichkeit, einen Rollback durchzuführen. Infolgedessen sind Commits in diesem Kontext nicht erlaubt und es wird versucht, ein COMMIT
auszuführen führt zu einem Fehler „kann nicht festgeschrieben werden, während eine Untertransaktion aktiv ist“.
Aus diesem Grund sehen Sie, wie Ihre Prozedur zum Exception-Handler springt, anstatt die raise notice 'B'
auszuführen :wenn es den commit
erreicht , es wirft einen Fehler und der Handler fängt ihn ab.
Dies ist jedoch ziemlich einfach zu umgehen. BEGIN ... END
Blöcke können verschachtelt werden, und nur Blöcke mit EXCEPTION
-Klauseln beinhalten das Setzen von Sicherungspunkten, sodass Sie die Befehle vor und nach dem Commit einfach in ihre eigenen Ausnahmebehandlungsroutinen einschließen können:
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;
$$;
Leider führt dies zu vielen Duplizierungen in den Fehlerbehandlungsroutinen, aber mir fällt kein guter Weg ein, dies zu vermeiden.