Semantik PL/pgSQL penanganan kesalahan tentukan bahwa:
Ini diimplementasikan menggunakan subtransaksi, yang pada dasarnya sama dengan savepoints . Dengan kata lain, ketika Anda menjalankan kode PL/pgSQL berikut:
BEGIN
PERFORM foo();
EXCEPTION WHEN others THEN
PERFORM handle_error();
END
...yang sebenarnya terjadi adalah seperti ini:
BEGIN
SAVEPOINT a;
PERFORM foo();
RELEASE SAVEPOINT a;
EXCEPTION WHEN others THEN
ROLLBACK TO SAVEPOINT a;
PERFORM handle_error();
END
Sebuah COMMIT
di dalam blok akan menghancurkan ini sepenuhnya; perubahan Anda akan dibuat permanen, savepoint akan dibuang, dan handler pengecualian akan dibiarkan tanpa cara untuk memutar kembali. Akibatnya, komit tidak diizinkan dalam konteks ini, dan mencoba menjalankan COMMIT
akan menghasilkan kesalahan "tidak dapat melakukan saat subtransaksi aktif".
Itulah mengapa Anda melihat prosedur Anda melompat ke handler pengecualian alih-alih menjalankan raise notice 'B'
:ketika mencapai commit
, itu melempar kesalahan, dan pawang menangkapnya.
Ini cukup mudah untuk diatasi. BEGIN ... END
blok dapat disarangkan, dan hanya blok dengan EXCEPTION
klausa melibatkan pengaturan savepoints, jadi Anda bisa membungkus perintah sebelum dan sesudah komit di penangan pengecualian mereka sendiri:
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;
$$;
Sayangnya, hal itu menyebabkan banyak duplikasi pada penangan kesalahan, tetapi saya tidak dapat memikirkan cara yang baik untuk menghindarinya.