De semantiek van PL/pgSQL's foutafhandeling dicteer dat:
Dit wordt geïmplementeerd met behulp van subtransacties, die in principe hetzelfde zijn als savepoints . Met andere woorden, wanneer u de volgende PL/pgSQL-code uitvoert:
BEGIN
PERFORM foo();
EXCEPTION WHEN others THEN
PERFORM handle_error();
END
...wat er feitelijk gebeurt is zoiets als dit:
BEGIN
SAVEPOINT a;
PERFORM foo();
RELEASE SAVEPOINT a;
EXCEPTION WHEN others THEN
ROLLBACK TO SAVEPOINT a;
PERFORM handle_error();
END
Een COMMIT
binnen het blok zou dit volledig breken; uw wijzigingen zouden permanent worden gemaakt, het opslagpunt zou worden weggegooid en de uitzonderingshandler zou geen manier meer hebben om terug te draaien. Als gevolg hiervan zijn commits niet toegestaan in deze context en wordt geprobeerd een COMMIT
. uit te voeren zal resulteren in een "kan niet vastleggen terwijl een subtransactie actief is" fout.
Daarom zie je je procedure naar de exception-handler springen in plaats van de raise notice 'B'
uit te voeren :wanneer het de commit
. bereikt , het geeft een fout en de handler vangt het op.
Dit is echter vrij eenvoudig om te omzeilen. BEGIN ... END
blokken kunnen worden genest, en alleen blokken met EXCEPTION
clausules omvatten het instellen van opslagpunten, dus je kunt de commando's voor en na de commit gewoon in hun eigen uitzonderingshandlers inpakken:
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;
$$;
Helaas leidt het tot veel dubbel werk in de foutafhandelingen, maar ik kan geen leuke manier bedenken om het te vermijden.