PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Kann ein Postgres-Commit in einer Prozedur vorhanden sein, die einen Ausnahmeblock hat?

Die Semantik der Fehlerbehandlung diktieren Sie das:

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.