ファイヤープロジェクト
エラー処理のためのコールバックの設定
2004-05-16T08:00+09:00   matsu
SQLを発行した結果がエラー,警告,選択行なしのいずれかの結果であった場合に,どんな処理をするかをあらかじめ登録することができる.
これまでのサンプルでは,以下のような似たようなエラー処理が何度も記述されていた.
if (memcmp (sqlca.sqlstate, "00", 2) != 0)
  {
    fprintf (stderr, "%s\n", sqlca.sqlerrm.sqlerrmc);
    exit (EXIT_FAILURE);
  }
だが,このような記述はソースを著しく損ねる場合がある.大体面倒臭い.そこで,あらかじめSQL文を実行した結果がエラー,警告,選択行なしの場合に何を行うかを登録できれば便利である.
EXEC SQL WHENEVER 結果 処理;
結果は以下の三つである.
SQLERROR
エラー.
SQLWARNING
警告.
NOT FOUND
選択行なし(SELECT文で行が選択されなかった).
処理には以下がある.
CONTINUE
とくに何もせず続行.デフォルト.
GOTO ラベル,GO TO ラベル
Cのgoto文により指定したラベルにとぶ.
SQLPRINT
stderrにメッセージを出力.
STOP
exit(1)を実行.
BREAK(※)
Cのbreak文を実行.ループかswitch文内で使用.
CALL 関数名 (引数), DO 関数名 (引数)
指定した引数で関数を呼び出す.引数がない場合は()内に何も記述しない.
CONTINUEとGOTO,GO TOは標準SQLでも定められているらしい.
※ ecpg 3.1.1ではなんかエラーになったが,GOTOで代替できるのでそんなに痛くはない.
エラー処理のコールバックを使用したサンプルを以下に示す.
このサンプルは,コールバックを設定し,不正なSQL文を発行したり,行が選択されないSELECT文を発行したりするものである.実行してみた.
$> ./callback  -d tcp:postgresql://dbserver/firstdb -u matsu -p hogefuga
[13399]: ECPGdebug: set to 1
[13399]: ECPGconnect: opening database firstdb on dbserver port <DEFAULT> for user matsu
[13399]: ECPGexecute line 49: QUERY: INVALID SQL; on connection dbConnection
[13399]: ECPGexecute line 49: Error: ERROR:  parser: parse error at or near "invalid"
[13399]: raising sqlstate YE000 in line 49,
  ''parser: parse error at or near "invalid"' in line 49.'.
[13399]: ECPGtrans line 53 action = rollback connection = dbConnection
AFTER POINT
BEFORE while
[13399]: ECPGexecute line 62: QUERY: INVALID SQL; on connection dbConnection
[13399]: ECPGexecute line 62: Error: ERROR:  parser: parse error at or near "invalid"
[13399]: raising sqlstate YE000 in line 62,
  ''parser: parse error at or near "invalid"' in line 62.'.
[13399]: ECPGtrans line 65 action = rollback connection = dbConnection
AFTER while
[13399]: ECPGexecute line 71: QUERY: INVALID SQL; on connection dbConnection
[13399]: ECPGexecute line 71: Error: ERROR:  parser: parse error at or near "invalid"
[13399]: raising sqlstate YE000 in line 71,
  ''parser: parse error at or near "invalid"' in line 71.'.
IN callback function
[13399]: ECPGtrans line 72 action = rollback connection = dbConnection
[13399]: ECPGexecute line 77: QUERY: create table hoge ( id integer , name char ( 8 ))
  on connection dbConnection
[13399]: ECPGexecute line 77 Ok: CREATE
[13399]: ECPGexecute line 79: QUERY: select  *  from hoge 
  on connection dbConnection
[13399]: ECPGexecute line 79: Correctly got 0 tuples with 2 fields
[13399]: raising sqlcode 100 in line 79, 'No data found in line 79.'.
sql error No data found in line 79.
[13399]: ECPGtrans line 80 action = rollback connection = dbConnection
[13399]: ECPGtrans line 86 action = rollback connection = dbConnection
[13399]: ecpg_finish: Connection dbConnection closed.
サンプルソースを確認しつつ,"AFTER POINT","BEFORE while","AFTER while","IN callback function","sql error No data found in line 79."という出力とその前の出力を確認するとコールバック処理されていることがわかる.
本頁で記述しているecpgの機能はコールバックと記述してきたが,それはプログラマがそのように(ある程度)錯覚してコーディングできるという意味であって,ecpgの実装では実際にはコールバックではない.ecpgの処理はだいたい以下のようである.
  • ソースを「上から順に」なめる.Cの制御(関数呼び出しやif文など)は解析,解釈しない.とにかく「上から順に」なめる.
  • 埋め込みSQLのパターンをCに変換.
  • ecpgは処理状態を持っていて,その状態として埋め込みSQLで使用できる変数などが記憶,チェックされる.エラー処理時のコールバックもこの状態として記憶される.
  • 特定の埋め込みSQLのパターンをCに変換する際,エラー処理時のコールバックの状態応じたCのパターンを追記.
これで大体想像できるのは,プログラム実行時に条件に応じてコールバックを設定,変更するというのは,注意が必要ということである.できればecpgによる変換後のCコードもチェックしたいくらいだ.以下に危険なサンプルを示す.
警告の場合のコールバック処理を,aの値に応じて切替えているつもりのコードである.これはecpgによって以下のように変換される.
このように,意図に反して警告の場合のコールバック処理は常に異常終了(STOP)である.上述のecpgの動作からして,プログラム実行時に条件に応じてコールバックを設定,変更するというのは,その恩恵の割に面倒臭い場合がある.どうしてもやりたいなら,以下のようになるだろうか.
これをecpgで変換すると以下のようになる.
このパターンではCREATE TABLEが一つだけだったが,コールバックの設定のために,例えば数十行を丸コピするとかなると,このコールバックの目的のからして本末転倒となる.
matsu(C)
Since 2002
Mail to matsu