| 絶対に失敗しない組み込みSQLのエラー処理 | ||||||||
| ポール・コンテ著 実運用のアプリケーションでSQLが今まで以上に広く使用されるようになるにつれ、組み込みSQLステートメントについて、堅牢なエラー・チェックやエラー処理をすることの重要性がますます高まってきています。組み込みSQLステートメントをILE RPGやILE Cobolなどといった高級言語で記述する際は、実行可能なSQLステートメントすべてに関してそれが正常に完了したか否かを常にチェックし、予期していない条件が発生したら適切に処理しなければなりません。本稿ではこうしたタスクを簡単に行い、しかも絶対に失敗しないためのアドバイス、テクニック、コーディング・パターンをご紹介します。 組み込みSQLのプログラミングには簡単なルールがあります。プログラム中の実行可能なすべてのSQLステートメントの実行直後にSQL状態値をチェックすべし、というものです。SQLランタイムがSQLステートメントを実行しようと試みた後に呼び出し側のプログラムに戻ってくると、SQLランタイムはSqlStateというプログラム変数をSQL状態値にセットします。(Declare Cursorなどの組み込み宣言文やSet Optionなどのプリコンパイラ・ディレクティブはSQLランタイムが実行するわけではないので、SQL状態値がセットされることはありません。) SQL状態は5文字のコードでXXYYYという構造をしています。ここでXXとYYYは以下の通りです。 ・ XXはクラスを示します。 ・ YYYはサブクラスを示します。 クラス値の意味は以下の通りです。 ・ 00 - ステートメントは無条件で正常に実行されました。 ・ 01 - ステートメントは正常に実行されましたが警告があります。 ・ 02 - ステートメントが処理したデータはありません。 ・ 03〜ZZ - エラーのためステートメントの実行に失敗しました。 これらのクラスについては、いくつか気をつけなければならない点があります。「00」クラスには「00000」というSQL状態値しか含まれませんので、クラスだけでなく文字列全体を調べることができます。 「01」クラスのSQL状態値のいくつかに対しては、ホスト変数に関連付けられたヌル・インジケータ変数のなかに正の値が入り、カラム・レベルのさまざまな警告やエラーが起きたことを示すことがあります。カラム・レベルのエラーとして起こりうるものの中には、文字列や日付の切り捨て、算術エラー、文字変換エラー、データ・マッピング・エラーなどがあります。SQLリファレンス・マニュアルの「ホスト変数の参照」というトピックには、インジケータ変数に関する全設定が記載されています。これらはカラム固有の条件であるため、本稿で説明するステートメント・レベルのエラー・チェックに加え、アプリケーションに適したテストをコーディングする必要があります。 クラス値の全リストを図1に示します。最新のV5のSQLメッセージとコードというマニュアルにはSQL状態値の総一覧が記載されています。SQL状態値はIBM DB2ファミリー全体に渡って首尾一貫したものであるように設計されており、SQL 1999という業界標準をベースとしています。このトピックの詳細については、後述の「SqlState値とSqlCode値の探し方」を参照してください。 SQL状態をチェックする SQL状態値は常に5文字の文字列なので、値全体をテストしたり、クラスを示す部分の最初の2文字だけをテストしたりすることも容易にできます。プログラム中のSQL状態をチェックするための絶対に失敗しないRPG IVのパターンをいくつか紹介しますので、これを流用したり応用したりしてください。これらのパターンをILE Cobolプログラムに適用する際のアドバイスについては、後述する「ILE Cobolのエラー処理」を参照してください。 まず、SQL状態クラスのニーモニック(ILE RPGでいう名前付き定数)と、チェックの対象としたい値全体を宣言します。警告クラスであることを示す「01」という接頭辞を忘れずにつけてください。(Falseのニーモニックも含めておきました。これは後述するパターンの1つで使用します。)これらの宣言文を/Copyメンバーの中に入れることで、コードの再利用をしやすくできます。図2に例を示します。 状況によっては、上記のニーモニックといくつかの共通パターンを用いて、SQLステートメントの結果をチェックすることができます。「success」という結果だけに着目したいステートメントについては、以下の簡単なテストで十分でしょう。 If SqlState <> SqlStateOK ExSr SqlError EndIf たとえば、サブルーチンを実行する代わりにプロシージャを呼び出すことで、このパターンのバリエーションを使用できます。SQL状態と失敗したステートメントを記述している文字列を渡す方法を図3に示します。ループ中のFetchステートメントの後で、利用可能な行がこれ以上ないことや警告やエラーを検知するには、図4に示すテストを使用します。 このパターンは、正常に実行されたFetchという最も起こりうる場合をまず切り分けてくれます。次に、「02000」状態をテストすることで、「結果セットの終わり」条件を探します。こうした予期された状態をチェックした後に初めて、警告クラス中のSQL状態をチェックします。(SqlStateの最初の2文字は「01」です。)これ以外の結果はすべてエラーとして扱われます。「02」クラスにはSqlStateの値として「02000」以外に2つの値がありますので注意してください。ただし、その2つの値は通常のFetchステートメントでは出現しないので、このパターンでもまたエラーとして扱われます。 MoreRows論理変数(ILE RPGのインジケータ・タイプ)がFetchループ(コード例には載っていません)を制御します。MoreRowsは、ループに入る前にTrue ('1')に初期化されていなければなりません。SqlState自体を使用してFetchループを制御してはいけません。ループのスコープ中にある他のSQLステートメントやエラー処理ルーチンによって、SqlStateがリセットされる可能性があるからです。ここでも、サブルーチンではなくプロシージャを呼び出すことで、あるいは特定のクラスや状態用にテストを追加することで、このパターンを変更することができます。 InsertのようなSQLステートメントについては、特定のエラーを検出したいか、あるいは実行が正常に完了したのを知りたいだけであれば、図5に示すテストを使用してください。「Skip」の行は単なるコメント行であり、Insertが正常に実行されたときには、Selectの後の次のRPGステートメントに制御が単に移るという点に注意してください。 2番目のテストは、プライマリー・キーまたはユニークな制約、あるいはユニークなインデックスを持つ任意のカラムに対して、重複した値をインサートしようとすると、それを検知します。この特定の条件を検知することで、アプリケーションは使用しているユーザーに関連するフィードバックを提供することができ、ユーザーはトランザクションを回復させて完了させることができます。 3番目のテストはその他のすべての制約違反を検知し、アプリケーションが回復できない場合でも、より有用なフィードバックを提供できるようにします。重複したキー状態(「23505」)になっているか否かのテストは、特定の状態を含むクラス(「23」)のテストの前に実行しなければなりません。この順番が逆になると、「23505」という状態は「23」クラスのテストによって検知され、常に一般定数エラーとして処理されてしまいます。このパターンや2文字のクラス値と5文字の状態値を混ぜてテストする他のパターンでは、特定の状態値のいずれかを含むクラスをテストする前に、5文字の状態値をテストすることが重要です。 同様のアプローチを取ることで、When条件を追加してその他の特定のSQL状態やクラスを処理することができます。DeleteステートメントやUpdateステートメント用のパターンはInsertパターンをベースとすることができ、row-not-found(「02000」)やrow-locked(「57033」)などの関連するSQL状態のテストを含むことができます。 最後にちょっとしたアドバイスを。エラー処理にSQL Wheneverステートメントを使用しようなどとは、決して考えないでください。トラブルの元になります。これについては、「Wheneverは決して使用しない」(後述)で説明します。 応用編 本稿でご紹介したパターンをテンプレートとして使用することで、実行可能な各SQLステートメントの後に/Copy(あるいは/Include)できる部分的なコードの自分専用のコレクションを作成することができます。いくつかのバリエーションを検討したほうがよいSQLステートメントもあるでしょう。たとえば、ユーザーからのフィードバックやエラー回復処理がたくさんあるような対話型アプリケーションで使用されているFetchと、バッチ・ジョブ中のFetchに対しては、別の方法が必要になるかもしれません。 聡明な読者の中には、SqlState変数やその他の引数(SQLステートメント・タイプなど)を渡す汎用のプロシージャを呼び出し、そのプロシージャの中でさまざまなテストをコーディングするという単純な方法をなぜ推奨しないのかと、疑問に思われる方もおられるかもしれません。この方法でもうまくいくのですが、それ以降の実行フローを制御するためのアーキテクチャがずっと複雑にならざるを得ないのです。 私がご紹介したパターンに沿った/Copyコードのセクションの集合、サブルーチン中のアプリケーション固有のコードまたは標準名を持つサブ・プロシージャ、追加の診断情報の取得やログ・エントリの発行などといった共通の機能を提供する呼び出し可能なプロシージャ一式を結合する方が、より簡単であるし柔軟性も保てると思うのです。 またいずれかの機会に、SQL状態ばかりでなく追加の診断情報を取得する方法を調べ、SQLエラー処理を強化するテクニックをご紹介したいと思います。 ポール・コンテ氏はベル・データ鰍フ提携先、米Penton Media, Inc.が発行する月刊誌「SystemiNetwork」のシニア・テクニカル・エディタです。同氏はオレゴン州ユージーン市で研修およびコンサルティングを提供しているPCES社の取締役社長でもあります。
|
| ↑このページのトップへ |