本節では、埋め込みSQLプログラムにおいて、例外条件や警告をどのように扱うことができるかについて説明します。 このために、共に使用できる複数の機能があります。
エラーや警告を受け取る簡単な手法の1つは、特定の条件が発生する度に特定の動作を実行するように設定することです。 一般的には以下のようになります。
EXEC SQL WHENEVER condition action;
condition は以下のいずれかを取ることができます。
SQL文の実行中にエラーが発生する度に、指定した動作が呼び出されます。
SQL文の実行中に警告が発生する度に、指定した動作が呼び出されます。
SQL文が0行を受けとる、もしくは、0行に影響する時、指定した動作が呼び出されます。 (この条件はエラーではありませんが、これを特別に扱いたい場合があります。)
actionは以下のいずれかを取ることができます。
これは、実際の所、その条件が無視されることを意味します。 これがデフォルトです。
指定したラベルに移動します。 (C のgoto文を使用します。)
標準エラーにメッセージを出力します。 これは、単純なプログラムやプロトタイプ作成時に役に立ちます。 メッセージの詳細は設定できません。
プログラムを終了させるexit(1)を呼び出します。
Cのbreak文を実行します。 これはループ内、もしくは、switch文内でのみ使用しなければなりません。
指定した引数で、指定したC関数を呼び出します。
標準SQLではCONTINUEとGOTO (とGO TO)のみを提供しています。
簡単なプログラムで使用してみたくなるような例を以下に示します。 警告が発生した場合に簡単なメッセージを表示し、エラーが発生した場合にプログラムを中断します。
EXEC SQL WHENEVER SQLWARNING SQLPRINT; EXEC SQL WHENEVER SQLERROR STOP;
EXEC SQL WHENEVER文はCの文ではなく、SQLプリプロセッサのディレクティブです。 設定したエラーもしくは警告動作は、最初のEXEC SQL WHENEVERと条件を発生させたSQL文の間で、同一条件に異なる動作が設定されない限り、ハンドラを設定した所より後にある、全ての埋め込みSQL文に適用されます。 Cプログラムの制御フローは関係しません。 ですので、以下の2つのCプログラムの抜粋はどちらも望み通りの動作を行ないません。
/* * WRONG */ int main(int argc, char *argv[]) { ... if (verbose) { EXEC SQL WHENEVER SQLWARNING SQLPRINT; } ... EXEC SQL SELECT ...; ... }
/* * WRONG */ int main(int argc, char *argv[]) { ... set_error_handler(); ... EXEC SQL SELECT ...; ... } static void set_error_handler(void) { EXEC SQL WHENEVER SQLERROR STOP; }
より強力にエラーを扱うために、埋め込みSQLインタフェースは以下の構造体を持つsqlcaという名前のグローバル変数を提供します。
struct { char sqlcaid[8]; long sqlabc; long sqlcode; struct { int sqlerrml; char sqlerrmc[70]; } sqlerrm; char sqlerrp[8]; long sqlerrd[6]; char sqlwarn[8]; char sqlstate[5]; } sqlca;
(マルチスレッド化されたプログラムでは、各スレッドは自動的にsqlcaのコピーを独自に持ちます。 これは標準Cのerrnoグローバル変数の扱いと同様に動作します。)
sqlcaは警告とエラーの両方を対象としています。 1つの文の実行時に複数の警告やエラーが発生した場合、sqlcaは最後のものに関した情報のみを含みます。
直前のSQL文でエラーがなければ、sqlca.sqlcodeは0に、sqlca.sqlstateは"00000"になります。 警告やエラーが発生した場合は、sqlca.sqlcodeは負の値に、sqlca.sqlstateは"00000"以外になります。 正のsqlca.sqlcodeは、直前の問い合わせが0行を返したなどの無害な条件を示します。 sqlca.sqlcodeとsqlca.sqlstateは2つの異なるエラーコードスキーマです。 後で詳細に説明します。
直前のSQL文が成功すると、sqlca.sqlerrd[1]は処理された行のOIDが、もしあれば、格納されます。 また、もしそのコマンドで適切ならば、sqlca.sqlerrd[2]は処理された、もしくは返された行数が格納されます。
エラーもしくは警告の場合、sqlca.sqlerrm.sqlerrmcには、そのエラーを説明する文字列が格納されます。 sqlca.sqlerrm.sqlerrml フィールドにはsqlca.sqlerrm.sqlerrmcに格納されたエラーメッセージ長が格納されます。 (strlen()の結果です。おそらくはCプログラマは必要としないでしょう。) 一部のメッセージは固定長のsqlerrmc配列には長すぎることに注意してください。 この場合は切り詰められます。
警告の場合、sqlca.sqlwarn[2] は Wに設定されます。 (他の全ての場合では、これはW以外の何かに設定されます。) sqlca.sqlwarn[1] が Wに設定された場合、ホスト変数に代入する際に値が切り詰められています。 他の要素が警告を示すように設定された場合、sqlca.sqlwarn[0]はWに設定されます。
今の所、sqlcaid、sqlcabc、sqlerrp、ならびに、sqlerrdとsqlwarnの上記以外の要素は有用な情報を持ちません。
sqlcaは標準SQLでは定義されていません。 しかし、複数の他のSQLデータベースシステムで実装されています。 その定義は基本部分は似ていますが、移植性を持つアプリケーションを作成する場合は実装の違いを注意して調査しなければなりません。
sqlca.sqlstateとsqlca.sqlcodeはエラーコードを提供する異なる2つのスキーマです。 両者とも標準SQLで規定されていますが、SQLCODEは1992年版の標準では廃れたものとされ、1999年編で削除されました。 従って、新規アプリケーションではSQLSTATEを使用することを強く勧めます。
SQLSTATEは5要素の文字配列です。 この5文字は、各種のエラー条件、警告条件のコードを表現する数字、大文字から構成されます。 SQLSTATEは階層を持ったスキーマです。 最初の2文字は条件を汎化したクラスを示し、残り3文字は汎化クラスの副クラスを示します。 成功状態は00000というコードで示されます。 SQLSTATEコードのほとんどは標準SQLで定義されています。 PostgreSQLサーバは本質的にSQLSTATEエラーコードをサポートしています。 従って、全てのアプリケーションでこのエラーコードを使用することで、高度な一貫性を達成することができます。 詳細については付録Aを参照してください。
廃止されたエラーコードスキーマであるSQLCODEは単なる整数です。 0という値は成功を意味し、正の値は追加情報を持った成功を、負の値はエラーを示します。 標準SQLでは、直前のコマンドが0行を返す、もしくは0行に影響したことを示す100という値までの正の値のみを定義しています。 負の値は規定されていません。 従って、このスキーマでは低い移植性しか達成できず、また、コード体系も階層を持っていません。 歴史的に、PostgreSQLの埋め込みSQLプロセッサには、いくつかの特殊なSQLCODEの値が専用に割り当てられていました。 以下に、その数値とそのシンボル名の一覧を示します。 これらは他のSQL実装への移植性がないことを忘れないでください。 アプリケーションのSQLSTATEスキーマへの移行を簡易化するために、対応するSQLSTATEも示しています。 しかし、2つのスキーマ間の関係は1対1ではなく1対多です。 (実際は多対多です。) ですので、場合毎に付録Aに示したグローバルな各SQLSTATEを参照しなければなりません。
割り当て済みの SQLCODEです。
仮想メモリ不足を示します。(SQLSTATE YE001)
ライブラリが把握していない何かをプリプロセッサが生成したことを示します。 おそらく、互換性がないプリプロセッサとライブラリのバージョンを使用しています。 (SQLSTATE YE002)
コマンドの想定より多くのホスト変数が指定されたことを意味します。 (SQLSTATE 07001 もしくは 07002)
コマンドの想定よりも少ないホスト変数が指定されたことを意味します。 (SQLSTATE 07001 もしくは 07002)
問い合わせが複数行を返したが、文では1つの結果の格納の準備だけしかしていなかったことを意味します。 (例えば、指定された変数が配列ではなかった。) (SQLSTATE 21000)
ホスト変数の型がintですが、データベース内のデータ型が異なり、その値をintとして解釈させることができませんでした。 ライブラリはこの変換にstrtol()を使用します。 (SQLSTATE 42804)
ホスト変数の型がunsigned intですが、データベース内のデータ型が異なり、その値をunsigned intとして解釈させることができませんでした。 ライブラリはこの変換にstrtoul()を使用します。 (SQLSTATE 42804)
ホスト変数の型がfloatですが、データベース内のデータ型が異なり、その値をfloatとして解釈させることができませんでした。 ライブラリはこの変換にstrtod()を使用します。 (SQLSTATE 42804)
これは、ホスト変数の型がboolですが、データベース内のデータが't'でも'f'でもなかったことを意味します。 (SQLSTATE 42804)
PostgreSQLサーバに送信された文が空でした。 (通常埋め込みSQLプログラムでは発生しません。ですので、これは内部エラーを示しているかもしれません。) (SQLSTATE YE002)
ヌル値が返されましたが、NULL用の指示子変数が与えられていませんでした。 (SQLSTATE 22002)
配列が必要な所に普通の変数が使用されていました。 (SQLSTATE 42804)
配列値を要求した所にデータベースが普通の変数を返しました。 (SQLSTATE 42804)
存在しない接続にプログラムがアクセスしようとしました。 (SQLSTATE 08003)
存在するが開いていない接続にプログラムがアクセスしようとしました。 (これは内部エラーです。) (SQLSTATE YE002)
使用しようとした文が準備されていませんでした。 (SQLSTATE 26000)
指定した記述子が見つかりませんでした。 使用しようとした文は準備されていませんでした。 (SQLSTATE 33000)
記述子のインデックスが範囲外でした。 (SQLSTATE 07009)
無効な記述子項目が要求されました。 (これは内部エラーです。) (SQLSTATE YE002)
動的な文の実行時にデータベースが数値を返しましたが、ホスト変数が数値でありませんでした。 (SQLSTATE 07006)
動的な文の実行時にデータベースが数値以外を返しましたが、ホスト変数が数値でした。 (SQLSTATE 07006)
PostgreSQLサーバで何らかのエラーが発生しました。 このメッセージはPostgreSQLサーバからのエラーメッセージを含みます。
PostgreSQLサーバがトランザクションのコミットやロールバックを始めることができないことを通知しました。 (SQLSTATE 08007)
データベースへの接続試行に失敗しました。 (SQLSTATE 08001)
直前のコマンドが0行を返した、0行に影響した、カーソルの終端に達したといった、無害な条件を示します。 (SQLSTATE 02000)