例外処理
最終回となる今回は、ストアドプロシーシャの移行の説明のあと、全体のまとめを紹介します。
PostgreSQLには、Oracleにあるような型付けされた例外や例外ハンドラの機構は用意されていません。PostgreSQLのマニュアルには、例外処理機構について次のように説明されています。
…現時点では(データ型の書式エラー、浮動小数点エラー、解析エラーといったもののうち)、何が実際にその中断を引き起こしたのかを通知することは できません。そして、この段階ではバックエンドは不整合状態にあるので上位のエクゼキュータに戻したり、コマンドをさらに発行するとデータベース全体を壊 してしまうかもしれません。
したがって、現段階で関数やトリガプロシージャ実行中に中断が起きたときに、PL/pgSQLができることは、どの関数のどこ(行番号と文の型)で 発生したのか知らせるフィールドをいくつかメッセージに追加することだけです。このエラーは常に関数の実行を停止します。
この説明にあるように、PostgreSQLは処理が失敗した原因を報告することができません。このため、失敗の種類に応じた処理を取ることもでき ません。現時点で、PostgreSQLのRAISE文にできることは、単にエラーを発生させて、エラーを記録することだけです。
そこで、PL/SQLにおけるEXCEPTIONブロックは、そのまま削除することで対応するようにします。
PostgreSQL8.0では、EXCEPTION句を使って発生したエラーを捕捉することができるようになりました。エラーを捕捉すると、対応 するBEGIN ENDブロック内部のINSERTやUPDATE文などがロールバックされます。捕捉できるエラーの種類と名前はOracleとPostgreSQLで異 なりますので、詳しくはそれぞれのマニュアルを参照してください。下記は0による除算エラーを捕捉する場合の例です。
CREATE OR REPLACE PROCEDURE
CATCH_ERR_TEST( X IN NUMBER ) IS
Y NUMBER;
BEGIN
Y := 1 / X;
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('0除算エラーを捕捉しました。');
END;
下記が実行結果です。
BEGIN
CATCH_ERR_TEST(0);
END;
0除算エラーを捕捉しました。
これをPostgreSQLで表すと下記のようになります。
CREATE OR REPLACE FUNCTION CATCH_ERR_TEST(INTEGER)
RETURNS VOID AS '
DECLARE
x ALIAS FOR $1;
y integer;
BEGIN
y := 1 / x;
RETURN;
EXCEPTION
WHEN division_by_zero THEN
RAISE NOTICE ''0除算エラーを捕捉しました。'';
RETURN;
END;
' LANGUAGE 'plpgsql';
実行した結果は次のようになります。
% SELECT CATCH_ERR_TEST(0);
NOTICE: 0除算エラーを捕捉しました。
トランザクション処理
PostgreSQLのストアドプロシージャの内部では、トランザクションを開始したり、終了したりすることができません。つまり、ネストしたトランザクションに対応していないのです。また、セーブポイントにも対応していません。
これは、PostgreSQLのストアドプロシージャが、常に呼び出し側で確立した1つのトランザクションの中で実行されるためです。ただし、RAISE EXCEPTION文を使用してエラーを発生させ、現のトランザクションをアボートすることは可能です。
PostgreSQLのストアドプロシージャは、現段階では、長い業務ロジックを実装するのには向いていないといえるでしょう。
PostgreSQL8.0では、セーブポイントに対応しました。トランザクションの内部で部分的に処理をロールバックさせることが可能になり、長 い業務ロジックが実装しやすくなりました。詳しくはPostgreSQLマニュアルの「SAVEPOINT」および「ROLLBACKTO SAVEPOINT」などのSQLコマンドを参照してください。
動的SQL
PL/SQL、PL/pgSQLともに、動的に作成したSQL文を実行できる機能があります。 ただしPL/pgSQLでは、関数本体全体を1つの文字列として処理するしくみのため、注意が必要です。quote_ident(text)関数と、 quote_literal(text)関数による、文字列のエスケープ処理が必要になります。詳しくは、PostgreSQLのマニュアルを参照してく ださい。
前方参照
PL/pgSQLでは、ある関数中で別の関数名を前方参照することができます。PL/SQLのように、事前の宣言は必要ありません。これは、PL/pgSQLのストアドプロシージャの中で使われる識別子が、宣言時ではなく実行時に名前解決されるためです。
このため、PL/SQLにおけるサブプログラムの前方宣言は、次のように単純に無視することで移行します。
PROCEDURE MYPROC( A NUMBER );