相关文章推荐

WHENEVERディレクティブの使用について

デフォルトでは、プリコンパイルされたプログラムはOracleエラーおよび警告の状態を無視し、可能であれば処理を続行します。自動条件チェックおよびエラー処理を実行するにはWHENEVERディレクティブが必要です。

WHENEVERディレクティブによって、OracleでエラーやSQLERROR、SQLWARNINGまたはNOT FOUND条件が検出されたときのアクションを指定できます。これらのアクションには、次の文の継続実行、ルーチンのコール、ラベル付きの文への分岐、停止などがあります。

WHENEVERディレクティブの構文は次のとおりです。

EXEC SQL WHENEVER <condition> <action>; 

WHENEVERの条件

Oracleに自動的にSQLCAをチェックさせて、次の状態が存在しないかどうかを調べることができます。

SQLWARNING

sqlwarn[0] は、Oracleから警告( sqlwarn[1] から sqlwarn[7] までのいずれか1つも設定されます)が戻されたか、SQLCODEが+1403以外の正の値になっているために設定されます。たとえば、切り捨てられた列値が出力ホスト変数に割り当てられると、 sqlwarn[0] が設定されます。

MODE=ANSIのときは、SQLCAの宣言はオプションです。ただし、WHENEVER SQLWARNINGを使用するには、 必ず SQLCAを宣言してください。

NOT FOUND

OracleがWHERE句の検索条件を満たす行を検出できなかったか、SELECT INTOまたはFETCHが行を戻さなかったため、SQLCODEに+1403が設定されています(MODE=ANSIのときは+100)。

MODE=ANSIのときは、どの行もINSERTできなければ+100がSQLCODEに戻されます。

最初の実行SQL文の前に次のWHENEVERディレクティブを指定する必要があります。

EXEC SQL WHENEVER NOT FOUND GOTO close_cursor; 
EXEC SQL WHENEVER SQLWARNING CONTINUE; 
EXEC SQL WHENEVER SQLERROR GOTO error_handler; 

次の例では、WHENEVER...DOディレクティブを使用して特定のエラーを処理します。

EXEC SQL WHENEVER SQLERROR DO handle_insert_error("INSERT error"); EXEC SQL INSERT INTO emp (empno, ename, deptno)   VALUES (:emp_number, :emp_name, :dept_number); EXEC SQL WHENEVER SQLERROR DO handle_delete_error("DELETE error"); EXEC SQL DELETE FROM dept WHERE deptno = :dept_number; handle_insert_error(char *stmt) { switch(sqlca.sqlcode) case -1: /* duplicate key value */ break; case -1401: /* value too large */ break; default: /* do something here too */ break; handle_delete_error(char *stmt) printf("%s\n\n", stmt); if (sqlca.sqlerrd[2] == 0) /* no rows deleted */ { ...

SQLCAの変数をチェックしてアクションの過程を決定する手順に注意してください。

DO BREAKとDO CONTINUEの利用

次の例では、コミッションを受け取っている従業員の分のみ、従業員の名前、給料、コミッションを表示する方法を示しています。

#include <sqlca.h>
#include <stdio.h>
main()
    char *uid = "scott/tiger";
    struct { char ename[12]; float sal; float comm; } emp;
    /* Trap any connection error that might occur. */
    EXEC SQL WHENEVER SQLERROR GOTO whoops;
    EXEC SQL CONNECT :uid;
    EXEC SQL DECLARE c CURSOR FOR
        SELECT ename, sal, comm FROM EMP ORDER BY ENAME ASC;
    EXEC SQL OPEN c;
    /* Set up 'BREAK' condition to exit the loop. */
    EXEC SQL WHENEVER NOT FOUND DO BREAK;
   /* The DO CONTINUE makes the loop start at the next iteration when an error occurs.*/
    EXEC SQL WHENEVER SQLERROR DO CONTINUE;
    while (1)
          EXEC SQL FETCH c INTO :emp;
   /* An ORA-1405 would cause the 'continue' to occur. So only employees with */
   /* non-NULL commissions will be displayed. */
          printf("%s  %7.2f  %9.2f\n", emp.ename, emp.sal, emp.comm);
/* This 'CONTINUE' shuts off the 'DO CONTINUE' allowing the program to 
   proceed if any further errors do occur, specifically, with the CLOSE */
    EXEC SQL WHENEVER SQLERROR CONTINUE;
    EXEC SQL CLOSE c;
    exit(EXIT_SUCCESS);
whoops:
    printf("%.*s\n", sqlca.sqlerrm.sqlerrml, sqlca.sqlerrm.sqlerrmc);
    exit(EXIT_FAILURE);

WHENEVER文の適用範囲

WHENEVER文は宣言部のため、そのスコープは論理的なものではなく位置的なものになります。つまり、WHENEVER文はプログラム・ロジックの流れではなく、ソース・ファイル内で物理的にWHENEVER文に続く実行SQL文をすべてテストします。したがって、WHENEVERディレクティブはテストする最初の実行SQL文の前に指定する必要があります。

あるWHENEVERディレクティブは、同じ条件をチェックする別のWHENEVERディレクティブに置き換えられるまでの間は有効です。

次の例では、最初のWHENEVER SQLERRORディレクティブは2番目のものに置き換えられます。したがって、このディレクティブの制御はCONNECT文のみに適用されます。2番目のWHENEVER SQLERRORディレクティブは、step1からstep3への制御の流れに関係なく、UPDATE文およびDROP文の両方に適用されます。

step1: 
    EXEC SQL WHENEVER SQLERROR STOP; 
    EXEC SQL CONNECT :username IDENTIFIED BY :password; 
    goto step3; 
step2: 
    EXEC SQL WHENEVER SQLERROR CONTINUE; 
    EXEC SQL UPDATE emp SET sal = sal * 1.10; 
step3: 
    EXEC SQL DROP INDEX emp_index; 

WHENEVERのガイドライン

この項では、一般的な問題点を回避するためのガイドラインを示します。

通常、WHENEVERディレクティブはプログラムの最初の実行SQL文の前に指定します。この位置に指定したWHENEVERディレクティブはファイルの最後まで有効になるため、発生するすべてのエラーを確実にトラップできます。

データの終了条件の処理

カーソルを使用して行をフェッチするときは、プログラムでデータの終了条件を処理できるようにしておく必要があります。FETCHがデータを戻さないときは、プログラムは次のようにFETCHループを終了します。

EXEC SQL WHENEVER NOT FOUND DO break;
for (;;)
    EXEC SQL FETCH...
EXEC SQL CLOSE my_cursor; 

行が挿入されていない場合、INSERTはNOT FOUNDを戻します。この条件を取り上げない場合は、INSERTの前にEXEC SQL WHENEVER NOT FOUND CONTINUEを使用します。

EXEC SQL WHENEVER NOT FOUND DO break;
for(;;)
   EXEC SQL FETCH ...
   EXEC SQL WHENEVER NOT FOUND CONTINUE;
   EXEC SQL INSERT INTO ...
EXEC SQL CLOSE my_cursor;

無限ループの回避について

WHENEVER SQLERROR GOTOディレクティブが、実行SQL文を含むエラー処理ルーチンに分岐しているときに、そのSQL文にエラーが発生すると、プログラムが無限ループに陥るおそれがあります。無限ループを回避するには、次に示すようにSQL文の前にWHENEVER SQLERROR CONTINUEを記述します。

EXEC SQL WHENEVER SQLERROR GOTO sql_error; 
sql_error: 
    EXEC SQL WHENEVER SQLERROR CONTINUE; 
    EXEC SQL ROLLBACK WORK RELEASE; 

WHENEVER SQLERROR CONTINUE文を指定しなければ、ROLLBACKエラーが発生したときにこのルーチンが再び実行されるため、結果として無限ループに陥ります。

WHENEVER句は、注意して使用しないと問題が発生することがあります。たとえば、検索条件を満たす行がないためにDELETE文がNOT FOUNDを設定すると、次のコードは無限ループに陥ります。

/* improper use of WHENEVER */ 
EXEC SQL WHENEVER NOT FOUND GOTO no_more; 
for (;;) 
    EXEC SQL FETCH emp_cursor INTO :emp_name, :salary; 
no_more: 
    EXEC SQL DELETE FROM emp WHERE empno = :emp_number; 

次の例では、GOTOのターゲットを再設定することで、NOT FOUND条件を適切に処理します。

/* proper use of WHENEVER */ 
EXEC SQL WHENEVER NOT FOUND GOTO no_more; 
for (;;) 
    EXEC SQL FETCH emp_cursor INTO :emp_name, :salary; 
no_more: 
    EXEC SQL WHENEVER NOT FOUND GOTO no_match; 
    EXEC SQL DELETE FROM emp WHERE empno = :emp_number; 
no_match: 
 
推荐文章