36.8. エラー処理 #

本節では、埋め込みSQLプログラムにおいて、例外条件や警告をどのように扱うことができるかについて説明します。 このために、共に使用できる2つの機能があります。

36.8.1. コールバックの設定 #

エラーや警告を受け取る簡単な手法の1つは、特定の条件が発生する度に特定の動作を実行するように設定することです。 一般的には以下のようになります。

EXEC SQL WHENEVER condition action;

conditionは以下のいずれかを取ることができます。

SQLERROR #

SQL文の実行中にエラーが発生する度に、指定した動作が呼び出されます。

SQLWARNING #

SQL文の実行中に警告が発生する度に、指定した動作が呼び出されます。

NOT FOUND #

SQL文が0行を受け取る、もしくは0行に影響する時、指定した動作が呼び出されます。 (この条件はエラーではありませんが、これを特別に扱いたい場合があります。)

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ではCONTINUEGOTO(と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;
}

36.8.2. sqlca #

より強力にエラーを扱うために、埋め込み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.sqlcodesqlca.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に設定されます。

今のところ、sqlcaidsqlabcsqlerrpならびにsqlerrdsqlwarnの上記以外の要素は有用な情報を持ちません。

sqlcaは標準SQLでは定義されていません。 しかし、複数の他のSQLデータベースシステムで実装されています。 その定義は基本部分は似ていますが、移植性を持つアプリケーションを作成する場合は実装の違いを注意して調査しなければなりません。

ここでWHENEVERsqlcaを組み合わせて使用して、エラーが発生した時に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
===============

36.8.3. SQLSTATESQLCODE #

sqlca.sqlstatesqlca.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です。

0 (ECPG_NO_ERROR) #

エラーがないことを示す。(SQLSTATE 00000)

100 (ECPG_NOT_FOUND) #

これは、最後に実行したコマンドが取り出した、または、処理した行がゼロ行であったこと、あるいは、カーソルの最後であることを示す、害のない条件です。(SQLSTATE 02000)

以下のように、カーソルをループ内で処理する時、ループを中断する時を検知する方法として、このコードを使用することができます。

while (1)
{
    EXEC SQL FETCH ... ;
    if (sqlca.sqlcode == ECPG_NOT_FOUND)
        break;
}

しかし、WHENEVER NOT FOUND DO BREAKはこれを内部で効率的に行います。 このため、通常、外部で明示的に記述する利点はありません。

-12 (ECPG_OUT_OF_MEMORY) #

仮想メモリ不足を示します。 この数値は-ENOMEMとして定義します。 (SQLSTATE YE001)

-200 (ECPG_UNSUPPORTED) #

ライブラリが把握していない何かをプリプロセッサが生成したことを示します。 おそらく、互換性がないプリプロセッサとライブラリのバージョンを使用しています。 (SQLSTATE YE002)

-201 (ECPG_TOO_MANY_ARGUMENTS) #

コマンドの想定より多くのホスト変数が指定されたことを意味します。 (SQLSTATE 07001もしくは07002)

-202 (ECPG_TOO_FEW_ARGUMENTS) #

コマンドの想定よりも少ないホスト変数が指定されたことを意味します。 (SQLSTATE 07001もしくは07002)

-203 (ECPG_TOO_MANY_MATCHES) #

問い合わせが複数行を返したけれども、SQL文では1つの結果の格納の準備だけしかしていなかったことを意味します (例えば、指定された変数が配列ではなかった)。 (SQLSTATE 21000)

-204 (ECPG_INT_FORMAT) #

ホスト変数の型がintですが、データベース内のデータ型が異なり、その値をintとして解釈させることができませんでした。 ライブラリはこの変換にstrtol()を使用します。 (SQLSTATE 42804)

-205 (ECPG_UINT_FORMAT) #

ホスト変数の型がunsigned intですが、データベース内のデータ型が異なり、その値をunsigned intとして解釈させることができませんでした。 ライブラリはこの変換にstrtoul()を使用します。 (SQLSTATE 42804)

-206 (ECPG_FLOAT_FORMAT) #

ホスト変数の型がfloatですが、データベース内のデータ型が異なり、その値をfloatとして解釈させることができませんでした。 ライブラリはこの変換にstrtod()を使用します。 (SQLSTATE 42804)

-207 (ECPG_NUMERIC_FORMAT) #

ホスト変数の型がnumericですが、データベース内のデータ型が異なり、その値をnumericとして解釈させることができませんでした。 (SQLSTATE 42804)

-208 (ECPG_INTERVAL_FORMAT) #

ホスト変数の型がintervalであり、データベース内のデータが他の型であり、interval値として解釈することができない値を含みます。 (SQLSTATE 42804)

-209 (ECPG_DATE_FORMAT) #

ホスト変数の型がdateであり、データベース内のデータが他の型であり、date値として解釈することができない値を含みます。 (SQLSTATE 42804)

-210 (ECPG_TIMESTAMP_FORMAT) #

ホスト変数の型がtimestampであり、データベース内のデータが他の型であり、timestamp値として解釈することができない値を含みます。 (SQLSTATE 42804)

-211 (ECPG_CONVERT_BOOL) #

これは、ホスト変数の型がboolですが、データベース内のデータが't'でも'f'でもなかったことを意味します。 (SQLSTATE 42804)

-212 (ECPG_EMPTY) #

PostgreSQLサーバに送信されたSQL文が空でした (通常埋め込みSQLプログラムでは発生しません。ですので、これは内部エラーを示しているかもしれません)。 (SQLSTATE YE002)

-213 (ECPG_MISSING_INDICATOR) #

NULL値が返されましたが、NULL用の指示子変数が与えられていませんでした。 (SQLSTATE 22002)

-214 (ECPG_NO_ARRAY) #

配列が必要な箇所に普通の変数が使用されていました。 (SQLSTATE 42804)

-215 (ECPG_DATA_NOT_ARRAY) #

配列値が必要な箇所にデータベースが普通の変数を返しました。 (SQLSTATE 42804)

-216 (ECPG_ARRAY_INSERT) #

値を配列に挿入できません。 (SQLSTATE 42804)

-220 (ECPG_NO_CONN) #

存在しない接続にプログラムがアクセスしようとしました。 (SQLSTATE 08003)

-221 (ECPG_NOT_CONN) #

存在するが開いていない接続にプログラムがアクセスしようとしました (これは内部エラーです)。 (SQLSTATE YE002)

-230 (ECPG_INVALID_STMT) #

使用しようとしたSQL文がプリペアされていませんでした。 (SQLSTATE 26000)

-239 (ECPG_INFORMIX_DUPLICATE_KEY) #

重複キーエラー。一意性制約違反(Informix互換モード)。 (SQLSTATE 23505)

-240 (ECPG_UNKNOWN_DESCRIPTOR) #

指定した記述子が見つかりませんでした。 使用しようとしたSQL文はプリペアされていませんでした。 (SQLSTATE 33000)

-241 (ECPG_INVALID_DESCRIPTOR_INDEX) #

記述子のインデックスが範囲外でした。 (SQLSTATE 07009)

-242 (ECPG_UNKNOWN_DESCRIPTOR_ITEM) #

無効な記述子項目が要求されました。(これは内部エラーです。) (SQLSTATE YE002)

-243 (ECPG_VAR_NOT_NUMERIC) #

動的なSQL文の実行時にデータベースが数値を返しましたが、ホスト変数が数値でありませんでした。 (SQLSTATE 07006)

-244 (ECPG_VAR_NOT_CHAR) #

動的なSQL文の実行時にデータベースが数値以外を返しましたが、ホスト変数が数値でした。 (SQLSTATE 07006)

-284 (ECPG_INFORMIX_SUBSELECT_NOT_ONE) #

副問い合わせの結果が単一行ではありません(Informix互換モード)。 (SQLSTATE 21000)

-400 (ECPG_PGSQL) #

PostgreSQLサーバで何らかのエラーが発生しました。 このメッセージはPostgreSQLサーバからのエラーメッセージを含みます。

-401 (ECPG_TRANS) #

PostgreSQLサーバがトランザクションのコミットやロールバックを始めることができないことを通知しました。 (SQLSTATE 08007)

-402 (ECPG_CONNECT) #

データベースへの接続試行に失敗しました。 (SQLSTATE 08001)

-403 (ECPG_DUPLICATE_KEY) #

重複キーエラー。一意性制約違反。 (SQLSTATE 23505)

-404 (ECPG_SUBSELECT_NOT_ONE) #

副問い合わせの結果が単一行ではありません。 (SQLSTATE 21000)

-602 (ECPG_WARNING_UNKNOWN_PORTAL) #

無効なカーソル名が指定されました。 (SQLSTATE 34000)

-603 (ECPG_WARNING_IN_TRANSACTION) #

トランザクションが進行中です。 (SQLSTATE 25001)

-604 (ECPG_WARNING_NO_TRANSACTION) #

活動中(進行中)のトランザクションがありません。 (SQLSTATE 25P01)

-605 (ECPG_WARNING_PORTAL_EXISTS) #

既存のカーソル名が指定されました。 (SQLSTATE 42P03)