目次


DB2問題判別 習熟シリーズ

第8回 アプリケーションの問題判別

Comments

コンテンツシリーズ

このコンテンツは全#シリーズのパート#です: DB2問題判別 習熟シリーズ

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:DB2問題判別 習熟シリーズ

このシリーズの続きに乞うご期待。

セクション1: 概要

本編では、DB2データベースを伴うアプリケーションの開発および実行の一般的な問題を診断する方法について説明します。この回の概念を理解するためには、データベースの基礎、SQLエラー・メッセージ、アプリケーション・インターフェース(特にODBC、JDBC、OLEDB/ADO)の基本的な知識が必要です。このほか、SQLとデータ操作の基本概念の理解も必要です。

この回では、以下の種類のアプリケーションを構築および実行する際の問題判別に焦点を絞ります。

  • ODBCアプリケーション
  • JDBCアプリケーション
  • OLEDB/ADOアプリケーション
  • ストアード・プロシージャー

セットアップ

この回で紹介する例は、Windows®オペレーティング・システムにインストールされています。ただし、すべての概念および演習は、どのDB2分散システムにも対応します。

例を実際に試してみるには、事前に以下の作業を完了しておく必要があります。

  • DB2とサンプルをインストールします。
  • 1つのインスタンスと、DB2テスト・データベース(必要に応じて作成)にアクセスするための2つのIDを作成し、データベース内でオブジェクトを作成するためのアクセス権を設定します。このDB2サンプル・データベースは、本編の全体にわたって使用します。
  • C/C++、Java®、Microsoft® Visual Basicの各コンパイラーをインストールします。これらは、この回で紹介するすべての例を試す場合にのみ必要です。

著者について

Murray Chislettは、コンピューター・サイエンスの優等学位を持つ、IBMトロント研究所DB2テクニカル・サポート・チームのシニア・メンバーです。以前IBMのデータベース・テクノロジー分野の開発者だったMurrayは、DB2 UDBのエンジンおよびアプリケーション開発の専門知識を使って、世界中のお客様をサポートしています。現在は主に、IBMデータ管理プレミア・サービスのお客様を担当しています。

Murray Chislettの連絡先については、http://www.ibm.com/contact/employees/usのIBM Global Directoryをご覧ください。電子メール・アドレスを調べることができます。

セクション2: アプリケーションの構築

アプリケーションの動作のしくみ

アプリケーションやストアード・プロシージャーは、データベースのデータを操作するための一連の標準APIを使って、さまざまな言語で記述されます。

問題を診断するためには、DB2アプリケーションに含まれる層を理解することが重要です。多くの場合、ODBCと同等の機能を持つDB2コール・レベル・インターフェース(CLI)が、他のドライバーの下の層で使用されています。したがって、問題を判別するのに複数の層をデバッグしなければならない場合もあります。

下の図は、典型的なアプリケーション・フローを表しています。

コンパイル時に発生するエラーの原因の多くは、プログラミング・エラーか、アプリケーションの作成のために正しくセットアップされていない環境にあります。この回では、コンパイル・エラーとリンク・エラーの例をいくつか見ていきます。

DB2アプリケーションの作成および実行に関連するほとんどのエラーは、『DB2 Applicaiton Development』マニュアルや、お使いのプログラミング・インターフェースの関連マニュアルに従うことによって回避できます。アプリケーション開発のためのDB2製品マニュアルは、HTMLおよびPDFの両方の形式で、http://www.ibm.com/cgi-bin/db2www/data/db2/udb/winos2unix/support/v7pubs.d2w/en_mainに用意されています。

コンパイル・エラー

ここでは、C/C++で記述されたCLIアプリケーションのコンパイル・エラーの例を見てみます。以下のサンプル・プログラムを使用します。

Sample.c

次のコマンドを使って、Sample.Cをコンパイルします。

cl sample.c -I"%DB2PATH%\include" -link"%DB2PATH%\lib\db2cli.lib"

(お使いのコンパイラーで別のオプションが必要な場合は、このコマンドに変更を加える必要があります。また、Makeファイルを作成してsample.cプログラムをコンパイルすることもできます。)

プログラムをコンパイルすると、次のエラーが返されます。

    sample.c(22) : error C2198: 'SQLAllocHandle' : 
too few actual parameters

Sample.Cの22行目には、次のコードが含まれています。

sqlrc = SQLAllocHandle( SQL_HANDLE_ENV, NULL ) ;

CLIのマニュアルでSQLAllocHandleを調べると、この呼び出しが間違っていることがわかります。SQLAllocHandle呼び出しの説明は、http://www.ibm.com/cgi-bin/db2www/data/db2/udb/winos2unix/support/document.d2w/report?fn=db2v7l0sqll1401.htm#HDRFNAH1で参照できます。これは、あらゆる種類のプログラミングに共通の単純なエラーです。通常、コンパイル時に発生するこの種のエラーは、関連マニュアルを確認し、必要に応じてコードを修正することによって解決できます。

Sample.cの22行目を次のように変更してエラーを修正します。

sqlrc = SQLAllocHandle( SQL_HANDLE_ENV, SQL_NULL_HANDLE, NULL ) ;

変更後は、このプログラムを正しくコンパイルおよび実行できるはずです。変更後も失敗する場合は、『DB2 Application Development Guide』とお使いのコンパイラーのマニュアルを調べて、環境が正しくセットアップされているかどうかを確認します。成功した場合は、CLIアプリケーションを実行するための環境が正しくセットアップされていることになります。

リンク・エラー

DB2アプリケーションのコンパイラー・エラーやリンカー・エラーの多くは、他の種類のアプリケーションを作成するときに発生するものと同じです。リンクの段階で、実行可能ファイルをスムーズに作成できなくなることがあります。

環境が正しくインストールされていること、および正しいDB2ライブラリー(C:\Program Files\SQLLIB\LIBまたは\BINに含まれています)を使用していることを確認します。これが、コンパイルやリンクの問題を回避する最善の方法です。環境を正しくセットアップする方法については、お使いのプログラミング・インターフェースに関連する『DB2 Application Development』や、お使いのコンパイラーのマニュアルを参照してください。

以下は、環境の正しいセットアップに関連する一般的な要素の例です。

  • Makeファイルに正しいコンパイラー情報が含まれていることを確認します。
  • インクルード・ファイルやライブラリーのためのコンパイラー関連の変数をすべて設定します。
  • 以下は、リンカー・エラーの原因となる一般的な間違いの例です。

以下は、リンカー・エラーの原因となる一般的な間違いの例です。

  • API/関数の呼び出しが間違っている
  • LIBPATHにリンク・ライブラリーの複数のバージョンがある
  • API/関数の定義が、ライブラリー・ファイルに含まれているコードと対応していない

DB2に同梱されているサンプルも見てみてください。これらのサンプルには、それらをコンパイルおよびリンクするためのスクリプトや説明も含まれています。

セクション3: ODBCアプリケーション

概要

DB2アプリケーションで予期しない動作が起こる場合、問題の根本原因を特定するには、たいていトレースが最善の方法になります。DB2でサポートされているプログラミング・インターフェースの多くが、最終的にはCLIアプリケーション層を通過するということをよく覚えておいてください。JDBCやOLEDBの場合もそうです。したがって、CLIアプリケーションやODBCアプリケーションを記述したりサポートしたりしない場合でも、その動作のしくみやトレースの方法を理解しておくことが重要になります。

この回では、CLI、JDBC、およびOLEDBの各アプリケーションのトレースについて見ていきます。

トレース

ODBCアプリケーションは、DB2 CLIインターフェースを使ってDB2にアクセスします。DB2 CLIライブラリーもODBCライブラリーの1つです。ODBCアプリケーションを診断する際には、多くの場合、ODBCトレースまたはDB2 CLIトレースを使って問題を判別するのが最も簡単です。ODBCドライバー・マネージャーを使用している場合は、ODBCトレースを取得するための機能がたいてい用意されています。お使いのドライバー・マネージャーのマニュアルで、ODBCトレースを使用可能にする方法を調べてみてください。DB2 CLIトレースはDB2専用のトレースであり、多くの場合、汎用のODBCトレースより多くの情報が含まれます。この2つのトレースは通常よく似ており、アプリケーションからのすべてのCLI呼び出しのエントリー・ポイントとエグジット・ポイントが、それらの呼び出しのパラメーターや戻りコードも含めてリストされます。

CLIトレースを使用可能にするには、db2cli.iniファイルに以下のエントリーを追加します。db2cli.iniファイルは、C:\Program Files\SQLLIB\ディレクトリーにあります(Windowsシステムの場合)。

TRACEFLUSH=1
TRACEPATHNAME=C:\TEMP
TRACE=ON

これらのパラメーターの意味については、 http://www.ibm.com/cgi-bin/db2www/data/db2/udb/winos2unix/support/ document.d2w/report?fn db2v7l0clikeyw.htm#HDRHDCONFGの『DB2 Call Level Interface Guide and Reference』を参照してください。

db2cli.iniファイルに追加できるキーワードはたくさんありますが、それらはアプリケーションの動作に影響を与える可能性があります。こうしたキーワードは、アプリケーションの問題を解決する場合もあれば、問題の原因になる場合もあります。中には、CLIのマニュアルで取り上げられていないキーワードもあります。このようなキーワードは、DB2サービスとサポートからしか利用できません。マニュアルに記載されていないキーワードがdb2cli.iniファイルにあった場合は、DB2サポート・チームによって推奨されているキーワードであると考えられます。

CLIトレースの使用可能化

前のページの説明に従って、CLIトレースをオンにします。以下は、db2cli.iniファイルのエントリーの例です。

; Comment lines start with a semi-colon.

[tstcli1x]
uid=userid
pwd=password
autocommit=0
TableType="'TABLE','VIEW','SYSTEM TABLE'"

[tstcli2x]
; Assuming dbalias2 is a database in DB2 for MVS.
SchemaList="'OWNER1','OWNER2',CURRENT SQLID"

[MyVeryLongDBALIASName]
dbalias=dbalias3
SysSchema=MYSCHEMA

[DWCTRLDB]
DBALIAS=DWCTRLDB

[COMMON]
trace=1
traceflush=1
Tracefilename=C:\temp\cli.trc

トレース機能を使ってアプリケーションの問題を診断する際には、アプリケーションのパフォーマンスに影響するという点に注意してください。現在のテスト・アプリケーションだけでなく、すべてのアプリケーションが影響を受けます。問題を特定できたら、忘れずにトレースをオフにしてください。

アプリケーション・エラー

以下のサンプル・アプリケーションには、実行時の問題があります。

Sample1.c

このアプリケーションは、実行すると、現在のユーザーIDとパスワードを使って、sampleという名前のデータベースに接続します。

このプログラムを修正して、データベースの名前を、ここでの実習で実際に使用しているデータベースの名前に変更する必要があります。

次のコマンドを使って、Sample1.cをコンパイルします(お使いのC/C++コンパイラーに合わせてコマンドを変更しなければならない場合もあります)。

   cl sample1.c -I"%DB2PATH%\include"  -link"%DB2PATH%\lib\db2cli.lib"

上のコンパイルのステップで作成された実行可能ファイルを呼び出して、プログラムをデータベースに対して実行します。その結果、次のエラーが返されます。

   Error Fetching.

Sample1.Cを調べて、アプリケーションがこのエラーを返す場所を見つけます。アプリケーションが上のエラーを返すためには、SQLFetch呼び出しがSQL_ERRORを返している必要があることがわかります。このアプリケーションはエラー情報を要求していないため、原因を解明するためにはより多くの情報が必要です。

前のセクションで使用可能にしてあるため、CLIトレースを調べることができます。db2cli.iniファイルのTRACEFILENAMEキーワードで指定したファイルにトレースが記録されています。このCLIトレースの、SQLFetch呼び出しを含む部分を調べます。

SQLFetch( hStmt=1:1 )
    ---> Time elapsed - +2.200000E-004 seconds

SQLFetch( )
    <--- SQL_ERROR   Time elapsed - +1.238000E-003 
    seconds

エラーが返されていることはわかりますが、エラーの詳細はわかりません。

ODBCエラーの詳細は、アプリケーションがその情報を要求した場合にのみ返されます。 CLIトレースを取得することによって、アプリケーションから返されるより多くの情報が得られる場合もありますが、上の例では、そうではありませんでした。次は、問題を診断するのに十分な情報がアプリケーションから返されるようにしてみます。

正しいエラー処理

トレースからは詳しい情報が得られなかったので、今度は、エラー情報を要求するために必要な呼び出しを追加します。

アプリケーションが十分なエラー情報を提供しないためにトラブルシューティングが困難になることはよくあります。正しいエラー情報を収集するためにサンプル・プログラムを書き直す場合は、サーバーから情報を取得するためのSQLGetDiagRec呼び出しを追加する必要があります。以下は、呼び出しに失敗した場合にDB2エラー情報を収集できるように、新しい関数を追加して書き直したSample2.cです。変更内容を確認してみてください。

Sample2.c

前にsample1を実行したときに作成されたcli.trcファイルを削除します。

次に、sample2.cをsample1.cのときと同じようにコンパイルし、作成された新しいプログラムを実行します。

次のエラーが返されます。

Error Fetching.

  SQLSTATE          = HY010
  Native Error Code = -99999
[IBM][CLI Driver] CLI0125E  Function sequence error. 
SQLSTATE=HY010
-------------------------

 Application Complete.

sample2.cの実行によって作成された新しいcli.trcファイルでも、このエラーを確認できます。このファイルは前と同じ場所にあります。ファイルを開き、エラーを返したSQLFetch呼び出しに関連する部分だけを調べます。

SQLFetch( hStmt=1:1 )
    ---> Time elapsed - +2.130000E-004 seconds

SQLFetch( )
    <--- SQL_ERROR   Time elapsed - +2.769000E-003 
    seconds

SQLGetDiagRec( fHandleType=SQL_HANDLE_STMT, 
hHandle=1:1, iRecNumber=1, pszSqlState=&0012ff10, 
pfNativeError=&0012fb08, pszErrorMsg=&0012fb0c, 
cbErrorMsgMax=1025, pcbErrorMsg=&0012ff1c )
    ---> Time elapsed - +5.350000E-004 seconds

SQLGetDiagRec( pszSqlState="HY010", 
pfNativeError=-99999, pszErrorMsg="[IBM][CLI Driver] 
CLI0125E  Function sequence error. SQLSTATE=HY010" -
X"5B49424D5D5B434C49204472697665725D20434C4930313235
45202046756E6374696F6E2073657175656E6365206572726F72
2E2053514C53544154453D4859303130", pcbErrorMsg=67 )
    <--- SQL_SUCCESS   Time elapsed - +3.274000E-003 
    seconds

SQLGetDiagRec( fHandleType=SQL_HANDLE_STMT, 
hHandle=1:1, iRecNumber=2, pszSqlState=&0012ff10, 
pfNativeError=&0012fb08, pszErrorMsg=&0012fb0c, 
cbErrorMsgMax=1025, pcbErrorMsg=&0012ff1c )
    ---> Time elapsed - +1.142700E-002 seconds

SQLGetDiagRec( )
    <--- SQL_NO_DATA_FOUND   Time elapsed - 
    +2.361000E-003 seconds

ODBC標準またはDB2 CLIのマニュアルを調べると、先にSQLExecuteを呼び出さないとSQLFetchが関数シーケンス・エラーを返すことがわかります。SQLFetchでデータをフェッチするためには、先にこのステートメントを実行する必要があります。

SQLExecuteの呼び出しを追加して、このsample2.cのプログラミング・エラーを修正します。その後、プログラムを再コンパイルおよび再実行して、変更が正しいことを確認します。

セクション4: JDBCアプリケーション

トレース

JDBCインターフェースは、DB2 CLIインターフェースを利用してDB2にアクセスします。JDBCアプリケーションのデバッグでは、しばしば複数のトレース機能を使用することになります。

JDBCの問題を解決するためには、db2cli.iniファイルにJDBCトレースのキーワードを追加して、JDBCトレースを使用可能にします。追加したキーワードは太字になっています。JDBCトレースを使用可能にした場合、db2cli.iniファイルは次のようになります。

; Comment lines start with a semi-colon.

[tstcli1x]
uid=userid
pwd=password
autocommit=0
TableType="'TABLE','VIEW','SYSTEM TABLE'"

[tstcli2x]
; Assuming dbalias2 is a database in DB2 for MVS.
SchemaList="'OWNER1','OWNER2',CURRENT SQLID"

[MyVeryLongDBALIASName]
dbalias=dbalias3
SysSchema=MYSCHEMA

[DWCTRLDB]
DBALIAS=DWCTRLDB

[COMMON]
trace=1
traceflush=1
Tracefilename=C:\temp\cli.trc

JDBCTRACE=1
JDBCFILENAME=C:\TEMP\JDBC.TRC
JDBCTRACE=FLUSH

ここでJDBCトレースを使用可能にする必要はありません。これは、お使いの環境でJDBCトレースを使用可能にする方法を示す例です。

デバッグ

この次の実習では、お使いの環境でJavaサンプル・プログラムとSQLステートメントを実行しても、ここで説明するものとまったく同じエラーが返されない場合があります。ここで紹介する例に従って、提供される情報からエラーの原因を見つけてください。

お使いの環境でsample3.javaを実行すると、不明な結果になります。

次のJDBCプログラムがあったとします。

sample3.java

ユーザーから、このプログラムについて問題が報告されました。ユーザーの報告によると、USER1がログインした状態でこのプログラムを実行すると、正常に実行されます。しかし、USER2がログインした状態で実行すると、オブジェクトUSER1.TABLEが定義されていないという内容のエラーが返されて失敗します。

コードを調べてみると、このアプリケーションが次のSQLステートメントを実行しようとしていることがわかります。

 Insert into TABLE values ('sample3')

アプリケーションの中からSQLを実行しようとしているときにエラーが返される場合、たいていは、別のインターフェース(DB2のコマンド行など)から同じSQLを実行してみるのが非常に有効です。

この例の場合、アプリケーション内のものとまったく同じステートメントをコマンド行から実行しても、エラーは発生しません。この時点では、なぜアプリケーションが、アプリケーション内で指定されていないオブジェクトUSER2.TABLEを探しているのかはわかりません。この問題の調査を続けるには、失敗するアプリケーションのJDBCトレースを取得します。以下は、失敗するアプリケーションのJDBCトレースのサンプル・セクションです。上の問題を引き起こす原因を探してみてください。

jdbc.app.DB2Connection -> prepareStatement( insert 
into Test values ('sample3') ) (2002-11-25 00:24:22.53)
| jdbc.app.DB2PreparedStatement -> DB2Statement( con, 
1003, 1007 ) (2002-11-25 00:24:22.53)
| | jdbc.app.DB2PreparedStatement -> 
checkResultSetType( 1003, 1007 ) 
(2002-11-25 00:24:22.53)
| | jdbc.app.DB2PreparedStatement <- 
checkResultSetType() [Time Elapsed = 0.0] 
(2002-11-25 00:24:22.53)
| | 10: Peak statements = 1
| | 10: Statement Handle = 1:1
| jdbc.app.DB2PreparedStatement <- DB2Statement() 
[Time Elapsed = 0.01] (2002-11-25 00:24:22.54)
| jdbc.app.DB2PreparedStatement -> 
DB2PreparedStatement( "insert into Test values 
('sample3')", con, 1003, 1007 ) 
(2002-11-25 00:24:22.54)
| | jdbc.app.DB2PreparedStatement -> 
getStatementType( insert into Test values ('sample3') 
) (2002-11-25 00:24:22.54)
| | jdbc.app.DB2PreparedStatement <- getStatementType() 
returns STMT_TYPE_OTHER (26) [Time Elapsed = 0.0] 
(2002-11-25 00:24:22.54)
| | 10: maxNumParam = 0
| jdbc.app.DB2PreparedStatement <- 
DB2PreparedStatement() [Time Elapsed = 0.01] 
(2002-11-25 00:24:22.55)
jdbc.app.DB2Connection <- prepareStatement() 
[Time Elapsed = 0.02] (2002-11-25 00:24:22.55)

jdbc.app.DB2PreparedStatement -> execute() 
(2002-11-25 00:24:22.55)
| 10: Statement Handle = 1:1
| jdbc.app.DB2PreparedStatement -> execute2() 
(2002-11-25 00:24:22.55)
| | 10: Statement Handle = 1:1
| | 10: SQLExecute()- returnCode = -1
| | jdbc.DB2Exception -> DB2Exception() 
(2002-11-25 00:24:22.56)
| | | 10: SQLError = [IBM][CLI Driver][DB2/NT] 
SQL0204N  "USER1.TEST" is an undefined name.  
SQLSTATE=42704

| | |     SQLState = 42S02
| | |     SQLNativeCode = -204
| | |     LineNumber = 0
| | |     SQLerrmc = USER1.TEST
| | jdbc.DB2Exception <- DB2Exception() 
[Time Elapsed = 0.0] (2002-11-25 00:24:22.56)
| | 20: executed (Exit) = false
| jdbc.app.DB2PreparedStatement <- execute2() 
[Time Elapsed = 0.02] (2002-11-25 00:24:22.57)
jdbc.app.DB2PreparedStatement <- execute() returns 
false [Time Elapsed = 0.02] (2002-11-25 00:24:22.57)

このトレースで、関連している可能性がある唯一の情報は、アプリケーション自体が返したエラー・メッセージです。問題の根本原因を見つけるには、まだ診断情報が足りません。

前に述べたように、JDBCアプリケーションはCLIインターフェースも使用するため、JDBCアプリケーションのデバッグでは、両方の層を調べなければならない場合もあります。したがって、次のステップは、CLIトレースを使用可能にしてプログラムを実行することになります。次のファイルには、失敗するsample3.javaのCLIトレースが含まれています。

Cli.trc

上のトレースのSQLPrepareとSQLExecuteの呼び出しを調べます。

SQLPrepareW( hStmt=1:1, pszSqlStr="insert into Test values ('sample3')" -
 X"69006E007300650072007400200069006E0074006F00200054006500730074002000760
 061006C00750065007300200028002700730061006D0070006C006500330027002900", 
 cbSqlStr=35 )
    ---> Time elapsed - +5.211000E-003 seconds
( StmtOut="insert into Test values ('sample3')" )
SQLPrepareW( )
    <--- SQL_SUCCESS   Time elapsed - +2.277000E-003 seconds

SQLNumParams( hStmt=1:1, pcPar=&0006fa04 )
    ---> Time elapsed - +2.160000E-004 seconds

SQLNumParams( pcPar=0 )
    <--- SQL_SUCCESS   Time elapsed - +1.136000E-003 seconds

SQLExecute( hStmt=1:1 )
    ---> Time elapsed - +3.290000E-003 seconds
    sqlccsend( ulBytes - 256 )
    sqlccsend( Handle - 152786688 )
    sqlccsend( ) - rc - 0, time elapsed - +1.083000E-003
    sqlccrecv( )
    sqlccrecv( ulBytes - 163 ) - rc - 0, time elapsed - +2.020000E-004

SQLExecute( )
    <--- SQL_ERROR   Time elapsed - +5.108000E-003 seconds
SQLGetDiagFieldW( fHandleType=SQL_HANDLE_STMT, hHandle=1:1, iRecNumber=1, 
fDiagIdentifier=Unknown value 2467, pDiagInfo=&0006f8ec, cbDiagInfoMax=140, 
pcbDiagInfo=&0006f99a )
    ---> Time elapsed - +6.290000E-004 seconds

SQLGetDiagFieldW( pDiagInfo=5439573, pcbDiagInfo=20 )
    <--- SQL_SUCCESS   Time elapsed - +3.003000E-003 seconds

SQLGetDiagFieldW( fHandleType=SQL_HANDLE_STMT, hHandle=1:1, iRecNumber=1, 
fDiagIdentifier=Unknown value 2461, pDiagInfo=&0006f988, cbDiagInfoMax=4, 
pcbDiagInfo=&0006f99a )
    ---> Time elapsed - +2.130000E-004 seconds

SQLGetDiagFieldW( pDiagInfo=0, pcbDiagInfo=20 )
    <--- SQL_SUCCESS   Time elapsed - +2.255000E-003 seconds

SQLErrorW( hEnv=0:0, hDbc=0:0, hStmt=1:1, pszSqlState=&0006f978, 
pfNativeError=&0006f984, pszErrorMsg=&0006f0e8, cbErrorMsgMax=1024, 
pcbErrorMsg=&0006f98e )
    ---> Time elapsed - +2.100000E-004 seconds

SQLErrorW( pszSqlState="42S02 4y  " - X"34003200530030003200000034FFFFFF0
0000000", pfNativeError=-204, pszErrorMsg="[IBM][CLI Driver][DB2/NT] 
SQL0204N  "USER1.TEST" is an undefined name.  SQLSTATE=42704
" -  - 
X"5B00490042004D005D005B0043004C00490020004400720069007600650072005D005B0
04400420032002F004E0054005D002000530051004C0030003200300034004E0020002000
2200550053004500520031002E0054004500530054002200200069007300200061006E002
00075006E0064006500660069006E006500640020006E0061006D0065002E002000200053
0051004C00530054004100540045003D00340032003700300034000D000A00", 
pcbErrorMsg=88 )
    <--- SQL_SUCCESS   Time elapsed - +3.066000E-003 seconds

SQLGetDiagFieldW( fHandleType=SQL_HANDLE_STMT, hHandle=1:1, iRecNumber=1, 
fDiagIdentifier=Unknown value 2467, pDiagInfo=&0006f8ec, cbDiagInfoMax=140, 
pcbDiagInfo=&0006f99a )
    ---> Time elapsed - +1.824000E-003 seconds

SQLGetDiagFieldW( pDiagInfo=5439573, pcbDiagInfo=20 )
    <--- SQL_SUCCESS   Time elapsed - +2.603000E-003 seconds

SQLGetDiagFieldW( fHandleType=SQL_HANDLE_STMT, hHandle=1:1, iRecNumber=1, 
fDiagIdentifier=Unknown value 2461, pDiagInfo=&0006f988, cbDiagInfoMax=4, 
pcbDiagInfo=&0006f99a )
    ---> Time elapsed - +2.100000E-004 seconds

SQLGetDiagFieldW( pDiagInfo=0, pcbDiagInfo=20 )
    <--- SQL_SUCCESS   Time elapsed - +2.268000E-003 seconds

SQLErrorW( hEnv=0:0, hDbc=0:0, hStmt=1:1, pszSqlState=&0006f978, 
pfNativeError=&0006f984, pszErrorMsg=&0006f0e8, cbErrorMsgMax=1024, 
pcbErrorMsg=&0006f98e )
    ---> Time elapsed - +2.030000E-004 seconds

SQLErrorW( )
    <--- SQL_NO_DATA_FOUND   Time elapsed - +2.143000E-003 seconds

エラーの原因を示す情報はまだ見つかりません。この問題では、トレース全体の調査が必要です。

前に述べたように、db2cli.iniファイル内に置かれるCLIキーワードは、アプリケーションに影響を与える可能性があります。したがって、キーワードが設定された形跡を探す必要があります。キーワードは、SQLDriverConnectW呼び出しと一緒にCLIトレースに含まれます。次のCLIトレース・セクションのSQLDriverConnect呼び出しを見てみます。

SQLDriverConnectW( hDbc=0:1, hwnd=0:0, szConnStrIn=
"DSN=sample;UID=;PWD=", cbConnStrIn=20, szConnStrOut
=NULL, cbConnStrOutMax=0, pcbConnStrOut=NULL, 
fDriverCompletion=SQL_DRIVER_NOPROMPT )
    ---> Time elapsed - +8.110000E-004 seconds
    sqlccsend( ulBytes - 1616 )
    sqlccsend( Handle - 152786688 )
    sqlccsend( ) - rc - 0, time elapsed - 
    +8.190000E-004
    sqlccrecv( )
    sqlccrecv( ulBytes - 1262 ) - rc - 0, time 
    elapsed - +1.990000E-004
    sqlccsend( ulBytes - 599 )
    sqlccsend( Handle - 152786688 )
    sqlccsend( ) - rc - 0, time elapsed - 
    +4.410000E-004
    sqlccrecv( )
    sqlccrecv( ulBytes - 237 ) - rc - 0, time 
    elapsed - +1.180900E-002
( DBMS NAME="DB2/NT", Version="07.02.0005", 
Fixpack="0x23060105" )

( Application Codepage=1252, Database  Codepage=
1252, Char Send/Recv Codepage=1252, Graphic 
Send/Recv Codepage=1200, Application Char 
Codepage=1252, Application Graphic Codepage=1200 )

( StmtOut="SET CURRENT SCHEMA = 'USER1'" )
    sqlccsend( ulBytes - 220 )
    sqlccsend( Handle - 152786688 )
    sqlccsend( ) - rc - 0, time elapsed - 
    +3.990000E-004
    sqlccrecv( )
    sqlccrecv( ulBytes - 163 ) - rc - 0, time 
    elapsed - +4.920000E-004
( COMMIT=1 )

    sqlccsend( ulBytes - 196 )
    sqlccsend( Handle - 152786688 )
    sqlccsend( ) - rc - 0, time elapsed - 
    +4.660000E-004
    sqlccrecv( )
    sqlccrecv( ulBytes - 27 ) - rc - 0, time 
    elapsed - +1.920000E-004

SQLDriverConnectW( )
    <--- SQL_SUCCESS   Time elapsed - 
    +4.637400E-002 seconds
( DSN="SAMPLE" )

( UID=" " )

( PWD="*" )

( CURRENTSQLID="USER1" )

問題は、接続呼び出しに含まれているCURRENTSQLIDキーワードです。USER1によって使用されているdb2cli.iniファイルを調べると、次のエントリーが見つかります。

 CURRENTSQLID=USER1

ここから、USER2がCURRENTSQLIDキーワードをUSER1に設定していることがわかります。CURRENTSQLIDキーワードは、修飾されていないSQLステートメントに'USER1'をスキーマとして追加します。このように、たとえアプリケーションが正しいID (USER2)を使ってデータベースに接続し、コマンド行から実行した場合には成功したSQLステートメントを実行したとしても、実行時にアプリケーションに影響を与えうる要素はまだあります。

ここで取り上げたアプリケーションはJavaで記述されていたにもかかわらず、この問題は、CLIトレースを調べることによって解決されました。アプリケーションの動作のしくみや、問題が発生した場合に調べる場所について、すべての側面を理解することが重要だという理由はここにあります。場合によっては、一切のトレースを取得する前に、このシナリオのユーザーのdb2cli.iniファイルを調べることによって、このアプリケーションの問題を見つけられたかもしれません。たとえば、CLIトレースで'USER1'を検索することによっても、この問題を見つけることができます。

セクション5: OLEDB/ADOアプリケーション

OLEDB/ADOアプリケーション

OLEDB/ADOアプリケーションもまた、DB2 CLI層を通じてdb2データベースにアクセスします。したがって、このセクションでも、DB2 CLIトレースをオンのままにしておく必要があります。次の実習で使用するVisual Basicプログラムを実行する前に、前回のサンプルのCLIトレース(cli.trc)ファイルを削除してください。

OLEDBアプリケーションを使用できるのはWindowsオペレーティング・システムだけであり、OLEDBアプリケーションの開発で最もよく使われる言語は、ADOインターフェースを使用するVisual Basicです。実行時の問題を見つけるのに必要なすべての情報がエラーから明らかになることは、ほとんどありません。この種のアプリケーションでは、バイナリーの実行可能ファイルしかない場合、DB2 CLIトレースがデバッグのための最善の手段になります。

ユーザーID USER1を使ってテスト・データベースに接続し、次のコマンドを使って表を作成します。

CREATE TABLE TEST (C1 CHAR(20) NOT NULL PRIMARY KEY)

Visual Basicアプリケーションを作成し、それを使って以下のコードを実行します(Visual Basicアプリケーションを記述する際には、DB2 samplesディレクトリーに含まれているサンプルを利用できます)。

Sub Main()
    
  Dim db As Connection
  On Error GoTo DisplayError
  Set db = New Connection
  db.CursorLocation = adUseClient
  db.Open "PROVIDER=IBMDADB2;dsn=SAMPLE;uid=;pwd=;"

  db.Execute "insert into test values ('VBSample')"
  MsgBox "Success!"
  Exit Sub
  
DisplayError:
     MsgBox Err.Description

End Sub

上のコードを更新して、dsnをSAMPLEからお使いのテスト・データベースの名前に変更する必要があります。

このアプリケーションの実行に成功すると、「Success!」と書かれたポップアップ・ウィンドウが表示されます。その後、2度目にアプリケーションを実行すると、今度は次のエラーが表示されて失敗します。

[DB2/NT] SQL0803N  One or more values in the INSERT statement, 
UPDATE statement, or foreign key update caused by a DELETE 
statement are not valid because the primary key, unique constraint 
or unique index identified by "1" constrains tabl "

少なくとも1度は成功したアプリケーションが、その後に失敗して驚くこともあります。この例の場合は、エラー・メッセージから十分な説明が得られます - 表の主キー制約に違反したことがエラーの原因です。

SQL0803エラーの詳細については、 http://www.ibm.com/cgi-bin/db2www/data/db2/udb/winos2unix/support/document.d2w/ report?fn=db2v7m0sql0800.htm#HDRSQL0800の『Messages Reference』の説明を参照してください。

この例ではアプリケーションによってエラーが返されましたが、このエラーは、CLIトレース機能を使って見つけることもできます。多くの場合、アプリケーション・エラーの根本原因を特定する方法は複数あります。

セクション6: ストアード・プロシージャー

ストアード・プロシージャー・ビルダーを使ったストアード・プロシージャーの作成

ストアード・プロシージャーは、データベース・エンジンの中で実行されるため、おそらく最もデバッグが難しいアプリケーションです。エラー処理ロジックを含む堅固なプロシージャーを記述することが最善の手法となります。

このセクションでは、USER1とUSER2という2つのユーザーIDが必要になります。ここで取り上げる問題のセットアップの一部として、プロシージャーを作成してテストする必要があります。

ID USER1を使用してデータベースに接続し、次のDDLを実行して、この例で必要な表を作成します。

Create table Table table1 (C1 char(20))
Insert into table1 values ( 'Row 1' )
Insert into table1 values ( 'Row 2' )

ストアード・プロシージャー・ビルダーを起動し、プロジェクト・ファイルを開くかまたは新しく作成して、JDBCストアード・プロシージャーを作成します。このストアード・プロシージャーを呼び出す際には、USER1を使ってデータベースに接続します。プロシージャーにStoredProc.GetDataという名前を付けて、結果セットを返すようにします。[SQLStatement]パネルで、次のステートメントを入力します。

Select C1 from table1

[options]パネルで、プロシージャーの固有名としてGetDataを、JarIDとしてStoredProcを、それぞれ指定します。

完了すると、正しくビルドされるはずです。次のステートメントを使って、DB2コマンド行からプロシージャーを実行してみてください。

Call storedproc.getdata()

正しく実行され、結果としてTable1の内容が返されるはずです。

C1
Row 1
Row 2

"GETDATA" RETURN_STATUS: "0"

これまでのところ、このストアード・プロシージャーはこれで完成したと言えます。

ストアード・プロシージャーのソース・ファイル

作成したプロシージャーへのアクセスをUSER1とUSER2だけに許可したいとします。Windowsエクスプローラーを使ってSTOREDPROC.jarファイルを見つけて(\Program Files\sqllib\fuction\jarディレクトリーにあります)、ファイルのアクセス許可を変更します。USER1とUSER2のみにフル・コントロールのアクセス許可を与え、ほかには誰もこのファイルにアクセスできないようにします。

次に、USER2としてデータベースに接続し、先ほどと同じ呼び出しステートメントを使ってもう一度プロシージャーを実行してみます。

今度は、次のエラーが返されます。

SQL4304N  Java stored procedure or user-defined 
function "STOREDPROC.GETDATA", specific name 
"GETDATA" could not load Java class "GetData", 
reason code "1".  SQLSTATE=42724

ストアード・プロシージャーはデータベース・エンジンの中で実行されるため、プロシージャーを実行する実行可能プログラムへのアクセス権は、データベース・エンジンを実行するIDにも必要になります。このため、ストアード・プロシージャーへのアクセスを制御するには、ファイルのアクセス許可を操作するのではなく、GRANTコマンドとREVOKEコマンドを使用する必要があります(ただし、ストアード・プロシージャーのバイナリー・ファイルをデータベースにコピーする場合には、ファイルのアクセス許可を変更しなければならないこともあります。たとえば、ストアード・プロシージャー・ビルダー環境の外部でC/C++のストアード・プロシージャーを作成する場合、通常は、そのバイナリー・ファイルをストアード・プロシージャー・ディレクトリーにコピーすることになります。このとき、コピーしたファイルへのアクセスが制限されている場合は、そのファイルに実行のアクセス許可を追加する必要があります)。

ストアード・プロシージャーでは、このほか、ストアード・プロシージャーを見つけてロードすること自体もよく問題になります。プロシージャーが正しい場所にあること、およびファイルのアクセス許可が正しいことを確認することによって、ほとんどのSQL4304エラーを取り除くことができます。

ストアード・プロシージャーの実行

ファイルのアクセス許可を元に戻したら、コマンド行インターフェースからもう一度ストアード・プロシージャーを実行します。次のエラーが返されるはずです。

   SQL0204N  "DB2ADMIN.TABLE1" 
   is an undefined name.  SQLSTATE=42704

プロシージャーは、表の作成に使用したのと同じIDを使って作成され、エンジン・プロセスの下で実行されていても、デフォルト・スキーマとして現在のIDを使用します。

もう一度ストアード・プロシージャー・ビルダーを起動し、GetDataプロシージャーを開きます。SQLステートメントを修正して、スキーマUSER1を含める必要があります。新しいSQLステートメントは、次のようになります。

   Select c1 from user1.table1

ストアード・プロシージャーをもう一度コマンド行から実行します。今度は成功するはずです。

セクション7: まとめ

この回で学んだこと

CLI、Java、OLEDBなど、さまざまな種類のDB2アプリケーションを紹介し、それらがどのようにDB2データベースと連携するのかを説明しました。このほか、アプリケーションのデバッグに使用できるいくつかの方式と、それらを呼び出す方法についても説明しました。

DB2に含まれているサンプルを試してみてください。サンプルに変更を加えることによって、成功する場合と失敗する場合の両方を試すことができます。なお、その際には、DB2サンプル・データベースかテスト専用のデータベースを使用するようにして、実稼動環境で試すことは避けてください。

詳細情報

詳細については、『Application and Development Guide』や『Administration Guide』などのDB2製品のマニュアルを参照してください。

Certified for Supportプログラムに含まれているすべてのチュートリアルを完了してください。このシリーズのその他のチュートリアルに含まれている問題判別方法の中には、アプリケーション・エラーのデバッグに役立つものもあります(db2trace機能など)。

このほか、http://www.ibm.comを通じて利用できるさまざまな記事や、IBM Educationを通じて利用できるコースも、アプリケーションの開発やデバッグのスキルを磨くのに役立ちます。

DB2 Technical Support Webサイト(http://www.ibm.com/software/data/db2/udb/winos2unix/support)にあるDB2アプリケーションの技術情報も、アプリケーションのデバッグおよび開発を支援するための優れたリソースになります。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Information Management
ArticleID=322736
ArticleTitle=DB2問題判別 習熟シリーズ: 第8回 アプリケーションの問題判別
publish-date=11162005