Семантиката на обработка на грешки<на PL/pgSQL /a> диктуват, че:
Това се реализира с помощта на подтранзакции, които по същество са същите като точки за запис . С други думи, когато изпълните следния PL/pgSQL код:
BEGIN
PERFORM foo();
EXCEPTION WHEN others THEN
PERFORM handle_error();
END
...това, което всъщност се случва, е нещо като това:
BEGIN
SAVEPOINT a;
PERFORM foo();
RELEASE SAVEPOINT a;
EXCEPTION WHEN others THEN
ROLLBACK TO SAVEPOINT a;
PERFORM handle_error();
END
COMMIT
в рамките на блока би нарушил това напълно; вашите промени ще бъдат направени постоянни, точката за запис ще бъде отхвърлена и манипулаторът на изключения ще остане без начин да се върне назад. В резултат на това ангажиментите не са разрешени в този контекст и се опитват да изпълнят COMMIT
ще доведе до грешка „не може да се ангажира, докато е активна подтранзакция“.
Ето защо виждате как процедурата ви прескача към манипулатора на изключения, вместо да изпълнява raise notice 'B'
:когато достигне commit
, той извежда грешка и манипулаторът я хваща.
Това обаче е доста лесно да се заобиколи. BEGIN ... END
блокове могат да бъдат вложени и само блокове с EXCEPTION
клаузите включват задаване на точки за запис, така че можете просто да обвиете командите преди и след ангажимента в техните собствени манипулатори на изключения:
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;
$$;
За съжаление, това наистина води до много дублиране в манипулаторите на грешки, но не мога да измисля добър начин да го избегна.