本節では、埋め込みSQLプログラムにおいて、例外条件や警告をどのように扱うことができるかについて説明します。 このために、共に使用できる2つの機能があります。
WHENEVER
コマンドを使用して、警告条件、エラー条件を扱うようにコールバックを設定することができます。
sqlca
変数から入手することができます。
エラーや警告を受け取る簡単な手法の1つは、特定の条件が発生する度に特定の動作を実行するように設定することです。 一般的には以下のようになります。
EXEC SQL WHENEVERcondition
action
;
condition
は以下のいずれかを取ることができます。
action
は以下のいずれかを取ることができます。
CONTINUE
#これは、実際のところ、その条件が無視されることを意味します。 これがデフォルトです。
GOTO label
GO TO label
#
指定したラベルに移動します
(Cのgoto
文を使用します)。
SQLPRINT
#標準エラーにメッセージを出力します。 これは、単純なプログラムやプロトタイプ作成時に役に立ちます。 メッセージの詳細は設定できません。
STOP
#
プログラムを終了させるexit(1)
を呼び出します。
DO BREAK
#
Cのbreak
文を実行します。
これはループ内、もしくはswitch
文内でのみ使用しなければなりません。
DO CONTINUE
#
Cのcontinue
文を実行します。
これはループ文の中でのみ実行すべきものです。
実行した場合、制御の流れがループの先頭に戻ります。
CALL name
(args
)
DO name
(args
)
#
指定した引数で、指定したC関数を呼び出します。
(この使用法は通常のPostgreSQL構文でのCALL
およびDO
とは意味が異なります。)
標準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プログラムの抜粋はどちらも望み通りの動作を行いません。
/* * 間違い */ int main(int argc, char *argv[]) { ... if (verbose) { EXEC SQL WHENEVER SQLWARNING SQLPRINT; } ... EXEC SQL SELECT ...; ... }
/* * 間違い */ 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
(SQL通信領域)という名前のグローバル変数を提供します。
struct { char sqlcaid[8]; long sqlabc; long sqlcode; struct { int sqlerrml; char sqlerrmc[SQLERRMC_LEN]; } sqlerrm; char sqlerrp[8]; long sqlerrd[6]; char sqlwarn[8]; char sqlstate[5]; } sqlca;
(マルチスレッド化されたプログラムでは、各スレッドは自動的にsqlca
のコピーを独自に持ちます。
これは標準Cのerrno
グローバル変数の扱いと同様に動作します。)
sqlca
は警告とエラーの両方を対象としています。
1つのSQL文の実行時に複数の警告やエラーが発生した場合、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
、sqlabc
、sqlerrp
ならびにsqlerrd
とsqlwarn
の上記以外の要素は有用な情報を持ちません。
sqlca
は標準SQLでは定義されていません。
しかし、複数の他のSQLデータベースシステムで実装されています。
その定義は基本部分は似ていますが、移植性を持つアプリケーションを作成する場合は実装の違いを注意して調査しなければなりません。
ここでWHENEVER
とsqlca
を組み合わせて使用して、エラーが発生した時にsqlca
の内容を表示する、1つの例を示します。
これはおそらく、より「ユーザ向け」のエラー処理を組み込む前の、アプリケーションのデバッグまたはプロトタイプで有用です。
EXEC SQL WHENEVER SQLERROR CALL print_sqlca(); void print_sqlca() { fprintf(stderr, "==== sqlca ====\n"); fprintf(stderr, "sqlcode: %ld\n", sqlca.sqlcode); fprintf(stderr, "sqlerrm.sqlerrml: %d\n", sqlca.sqlerrm.sqlerrml); fprintf(stderr, "sqlerrm.sqlerrmc: %s\n", sqlca.sqlerrm.sqlerrmc); fprintf(stderr, "sqlerrd: %ld %ld %ld %ld %ld %ld\n", sqlca.sqlerrd[0],sqlca.sqlerrd[1],sqlca.sqlerrd[2], sqlca.sqlerrd[3],sqlca.sqlerrd[4],sqlca.sqlerrd[5]); fprintf(stderr, "sqlwarn: %d %d %d %d %d %d %d %d\n", sqlca.sqlwarn[0], sqlca.sqlwarn[1], sqlca.sqlwarn[2], sqlca.sqlwarn[3], sqlca.sqlwarn[4], sqlca.sqlwarn[5], sqlca.sqlwarn[6], sqlca.sqlwarn[7]); fprintf(stderr, "sqlstate: %5s\n", sqlca.sqlstate); fprintf(stderr, "===============\n"); }
結果は以下のようになります(ここでのエラーはテーブル名の誤記述によるものです。)。
==== sqlca ==== sqlcode: -400 sqlerrm.sqlerrml: 49 sqlerrm.sqlerrmc: relation "pg_databasep" does not exist on line 38 sqlerrd: 0 0 0 0 0 0 sqlwarn: 0 0 0 0 0 0 0 0 sqlstate: 42P01 ===============
SQLSTATE
対SQLCODE
#
sqlca.sqlstate
とsqlca.sqlcode
はエラーコードを提供する異なる2つの機構です。
共に標準SQLから派生されたものですが、SQLCODE
はSQL-92版では廃れたものとされ、以降の版から削除されました。
したがって、新規アプリケーションでは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
です。
ECPG_NO_ERROR
) #エラーがないことを示す。(SQLSTATE 00000)
ECPG_NOT_FOUND
) #これは、最後に実行したコマンドが取り出した、または、処理した行がゼロ行であったこと、あるいは、カーソルの最後であることを示す、害のない条件です。(SQLSTATE 02000)
以下のように、カーソルをループ内で処理する時、ループを中断する時を検知する方法として、このコードを使用することができます。
while (1) { EXEC SQL FETCH ... ; if (sqlca.sqlcode == ECPG_NOT_FOUND) break; }
しかし、WHENEVER NOT FOUND DO BREAK
はこれを内部で効率的に行います。
このため、通常、外部で明示的に記述する利点はありません。
ECPG_OUT_OF_MEMORY
) #
仮想メモリ不足を示します。
この数値は-ENOMEM
として定義します。
(SQLSTATE YE001)
ECPG_UNSUPPORTED
) #ライブラリが把握していない何かをプリプロセッサが生成したことを示します。 おそらく、互換性がないプリプロセッサとライブラリのバージョンを使用しています。 (SQLSTATE YE002)
ECPG_TOO_MANY_ARGUMENTS
) #コマンドの想定より多くのホスト変数が指定されたことを意味します。 (SQLSTATE 07001もしくは07002)
ECPG_TOO_FEW_ARGUMENTS
) #コマンドの想定よりも少ないホスト変数が指定されたことを意味します。 (SQLSTATE 07001もしくは07002)
ECPG_TOO_MANY_MATCHES
) #問い合わせが複数行を返したけれども、SQL文では1つの結果の格納の準備だけしかしていなかったことを意味します (例えば、指定された変数が配列ではなかった)。 (SQLSTATE 21000)
ECPG_INT_FORMAT
) #
ホスト変数の型がint
ですが、データベース内のデータ型が異なり、その値をint
として解釈させることができませんでした。
ライブラリはこの変換にstrtol()
を使用します。
(SQLSTATE 42804)
ECPG_UINT_FORMAT
) #
ホスト変数の型がunsigned int
ですが、データベース内のデータ型が異なり、その値をunsigned int
として解釈させることができませんでした。
ライブラリはこの変換にstrtoul()
を使用します。
(SQLSTATE 42804)
ECPG_FLOAT_FORMAT
) #
ホスト変数の型がfloat
ですが、データベース内のデータ型が異なり、その値をfloat
として解釈させることができませんでした。
ライブラリはこの変換にstrtod()
を使用します。
(SQLSTATE 42804)
ECPG_NUMERIC_FORMAT
) #
ホスト変数の型がnumeric
ですが、データベース内のデータ型が異なり、その値をnumeric
として解釈させることができませんでした。
(SQLSTATE 42804)
ECPG_INTERVAL_FORMAT
) #
ホスト変数の型がinterval
であり、データベース内のデータが他の型であり、interval
値として解釈することができない値を含みます。
(SQLSTATE 42804)
ECPG_DATE_FORMAT
) #
ホスト変数の型がdate
であり、データベース内のデータが他の型であり、date
値として解釈することができない値を含みます。
(SQLSTATE 42804)
ECPG_TIMESTAMP_FORMAT
) #
ホスト変数の型がtimestamp
であり、データベース内のデータが他の型であり、timestamp
値として解釈することができない値を含みます。
(SQLSTATE 42804)
ECPG_CONVERT_BOOL
) #
これは、ホスト変数の型がbool
ですが、データベース内のデータが't'
でも'f'
でもなかったことを意味します。
(SQLSTATE 42804)
ECPG_EMPTY
) #PostgreSQLサーバに送信されたSQL文が空でした (通常埋め込みSQLプログラムでは発生しません。ですので、これは内部エラーを示しているかもしれません)。 (SQLSTATE YE002)
ECPG_MISSING_INDICATOR
) #NULL値が返されましたが、NULL用の指示子変数が与えられていませんでした。 (SQLSTATE 22002)
ECPG_NO_ARRAY
) #配列が必要な箇所に普通の変数が使用されていました。 (SQLSTATE 42804)
ECPG_DATA_NOT_ARRAY
) #配列値が必要な箇所にデータベースが普通の変数を返しました。 (SQLSTATE 42804)
ECPG_ARRAY_INSERT
) #値を配列に挿入できません。 (SQLSTATE 42804)
ECPG_NO_CONN
) #存在しない接続にプログラムがアクセスしようとしました。 (SQLSTATE 08003)
ECPG_NOT_CONN
) #存在するが開いていない接続にプログラムがアクセスしようとしました (これは内部エラーです)。 (SQLSTATE YE002)
ECPG_INVALID_STMT
) #使用しようとしたSQL文がプリペアされていませんでした。 (SQLSTATE 26000)
ECPG_INFORMIX_DUPLICATE_KEY
) #重複キーエラー。一意性制約違反(Informix互換モード)。 (SQLSTATE 23505)
ECPG_UNKNOWN_DESCRIPTOR
) #指定した記述子が見つかりませんでした。 使用しようとしたSQL文はプリペアされていませんでした。 (SQLSTATE 33000)
ECPG_INVALID_DESCRIPTOR_INDEX
) #記述子のインデックスが範囲外でした。 (SQLSTATE 07009)
ECPG_UNKNOWN_DESCRIPTOR_ITEM
) #無効な記述子項目が要求されました。(これは内部エラーです。) (SQLSTATE YE002)
ECPG_VAR_NOT_NUMERIC
) #動的なSQL文の実行時にデータベースが数値を返しましたが、ホスト変数が数値でありませんでした。 (SQLSTATE 07006)
ECPG_VAR_NOT_CHAR
) #動的なSQL文の実行時にデータベースが数値以外を返しましたが、ホスト変数が数値でした。 (SQLSTATE 07006)
ECPG_INFORMIX_SUBSELECT_NOT_ONE
) #副問い合わせの結果が単一行ではありません(Informix互換モード)。 (SQLSTATE 21000)
ECPG_PGSQL
) #PostgreSQLサーバで何らかのエラーが発生しました。 このメッセージはPostgreSQLサーバからのエラーメッセージを含みます。
ECPG_TRANS
) #PostgreSQLサーバがトランザクションのコミットやロールバックを始めることができないことを通知しました。 (SQLSTATE 08007)
ECPG_CONNECT
) #データベースへの接続試行に失敗しました。 (SQLSTATE 08001)
ECPG_DUPLICATE_KEY
) #重複キーエラー。一意性制約違反。 (SQLSTATE 23505)
ECPG_SUBSELECT_NOT_ONE
) #副問い合わせの結果が単一行ではありません。 (SQLSTATE 21000)
ECPG_WARNING_UNKNOWN_PORTAL
) #無効なカーソル名が指定されました。 (SQLSTATE 34000)
ECPG_WARNING_IN_TRANSACTION
) #トランザクションが進行中です。 (SQLSTATE 25001)
ECPG_WARNING_NO_TRANSACTION
) #活動中(進行中)のトランザクションがありません。 (SQLSTATE 25P01)
ECPG_WARNING_PORTAL_EXISTS
) #既存のカーソル名が指定されました。 (SQLSTATE 42P03)