La semántica del manejo de errores
Esto se implementa mediante subtransacciones, que son básicamente lo mismo que savepoints . En otras palabras, cuando ejecuta el siguiente código PL/pgSQL:
BEGIN
PERFORM foo();
EXCEPTION WHEN others THEN
PERFORM handle_error();
END
...lo que realmente está pasando es algo como esto:
BEGIN
SAVEPOINT a;
PERFORM foo();
RELEASE SAVEPOINT a;
EXCEPTION WHEN others THEN
ROLLBACK TO SAVEPOINT a;
PERFORM handle_error();
END
UN COMMIT
dentro del bloque rompería esto por completo; sus cambios se harían permanentes, el punto de guardado se descartaría y el controlador de excepciones no tendría forma de retroceder. Como resultado, las confirmaciones no están permitidas en este contexto e intentar ejecutar un COMMIT
dará como resultado un error "no se puede confirmar mientras una subtransacción está activa".
Es por eso que ve que su procedimiento salta al controlador de excepciones en lugar de ejecutar el raise notice 'B'
:cuando llega al commit
, arroja un error y el controlador lo detecta.
Sin embargo, esto es bastante sencillo de solucionar. BEGIN ... END
los bloques se pueden anidar y solo los bloques con EXCEPTION
Las cláusulas implican establecer puntos de guardado, por lo que puede envolver los comandos antes y después de la confirmación en sus propios controladores de excepción:
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;
$$;
Desafortunadamente, genera mucha duplicación en los controladores de errores, pero no puedo pensar en una buena manera de evitarlo.