無料スクリプト配布のPHP.TO   PHPの実用的なtips PHPマニュアル MySQLマニュアル Apacheマニュアル PostgreSQLマニュアル マニュアル検索    

33.8. エラー処理

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

33.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 文内でのみ使用しなければなりません。

CALL name ( args )
DO name ( args )

指定した引数で、指定した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;
}

33.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.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データベースシステムで実装されています。 その定義は基本部分は似ていますが、移植性を持つアプリケーションを作成する場合は実装の違いを注意して調査しなければなりません。

ここで 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
===============

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

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 値として解釈することができない値を含みます。

-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)

-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)


powered by SEO.CUG.NET