A semântica do tratamento de erros ditar que:
Isso é implementado usando subtransações, que são basicamente as mesmas que savepoints . Em outras palavras, quando você executa o seguinte código PL/pgSQL:
BEGIN
PERFORM foo();
EXCEPTION WHEN others THEN
PERFORM handle_error();
END
... o que está realmente acontecendo é algo assim:
BEGIN
SAVEPOINT a;
PERFORM foo();
RELEASE SAVEPOINT a;
EXCEPTION WHEN others THEN
ROLLBACK TO SAVEPOINT a;
PERFORM handle_error();
END
Um
COMMIT
dentro do bloco quebraria isso completamente; suas alterações se tornariam permanentes, o ponto de salvamento seria descartado e o manipulador de exceção não teria como reverter. Como resultado, commits não são permitidos neste contexto, e tentar executar um COMMIT
resultará em um erro "não é possível confirmar enquanto uma subtransação estiver ativa". É por isso que você vê seu procedimento pular para o manipulador de exceção em vez de executar o
raise notice 'B'
:quando atinge o commit
, ele lança um erro e o manipulador o captura. Isso é bastante simples de contornar, no entanto.
BEGIN ... END
blocos podem ser aninhados e apenas blocos com EXCEPTION
cláusulas envolvem a configuração de pontos de salvamento, então você pode simplesmente envolver os comandos antes e depois do commit em seus próprios manipuladores de exceção: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;
$$;
Infelizmente, isso leva a muita duplicação nos manipuladores de erros, mas não consigo pensar em uma boa maneira de evitá-lo.