PL/pgSQLの
これは、サブトランザクションを使用して実装されます。サブトランザクションは、基本的にセーブポイント と同じです。 。つまり、次の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;
$$;
残念ながら、エラーハンドラーで多くの重複が発生しますが、それを回避するための良い方法は考えられません。