Symfoware(R) Server RDBユーザーズガイド 応用プログラム開発編 - FUJITSU -
目次 索引 前ページ次ページ

上へ第2章 データベースを処理する応用プログラムの開発の概要
上へ2.1 SQL埋込みCプログラムの作成方法

2.1.16 コールバック機能の使用方法

ここでは、コールバック機能の使用方法について説明します。

コールバック機能とは、応用プログラムでSQL文を実行するとき、あらかじめSQL文の種別に対して登録した関数を実行する機能です。コールバック機能を使用すると、SQL文の実行ログを採取したり、SQL文の実行を迂回することができます。また、コールバック関数にはSQL文の実行結果が通知されるため、デッドロックやコネクション切断などの特定のエラーが発生したSQL文の実行ログを採取することも可能になります。

■コールバック関数の概

コールバック機能を使用する場合は、コールバック関数とよばれる利用者定義関数を作成し、SQL文の種別ごとに登録します。これにより、応用プログラムでSQL文が実行されるたびに、登録されたコールバック関数が実行されます。

コールバック関数にはセションID(マルチスレッド環境で動作する応用プログラムの場合)、SQL文、実行結果および入出力データが通知されるため、利用者は必要に応じてデータを加工してください。

image

◆コールバック関

コールバック関数は、利用者が作成する関数です。関数名は利用者によって任意に指定できますが、復帰値や関数のパラメタは規定されています。

コールバック関数は、SQL文の処理の直前か直後かによって、入口コールバック関数または出口コールバック関数と呼ばれます。入口コールバックまたは出口コールバックは、コールバック関数の登録時に指定します。

入口コールバック関数は、復帰値によってその後の動作が異なります。入口コールバック関数がSQLRDB_CONTINUEを返した場合は、SQL文の処理を実行します。しかし、SQLRDB_NOCONTINUEを返した場合はSQL文の処理は実行されず、出口コールバック関数が実行されます。入口コールバック関数でエラーが発生した場合などSQL文の処理を実行したくない場合は、SQLRDB_NOCONTINUEを返してください。

出口コールバック関数には、SQL文の実行結果としてSQLSTATEおよびSQLMSGが通知されます。エラーが発生したSQL文の実行ログを採取する場合は、SQLSTATEおよびSQLMSGの情報を使用してください。ただし、応用プログラムでホスト変数SQLMSGが定義されていない場合は、SQLMSGは通知されません。また、入口コールバック関数には、SQLSTATEおよびSQLMSGは通知されません。

応用プログラムとデータを共有するため、コールバック関数に対し利用者定義領域のアドレスを設定することができます。コールバック関数の結果を応用プログラムに通知するには、利用者定義領域を使用してください。利用者定義領域のアドレスは、コールバック関数の登録時に指定します。

image

■コールバック関数の登録方

応用プログラムにコールバック関数を登録する方法には以下があります。

SQL文を実行する場面に合わせて、個別の処理が必要なコールバック関数の場合は、応用プログラムのプログラムソースに記述する方法で登録します。コールバック関数を登録するには、応用プログラムの修正およびコンパイルが必要になります。

応用プログラムの全体を通して、汎用的な処理を行うコールバック関数の場合は、動的ライブラリを使用する方法で登録します。応用プログラムの修正は必要ありません。

◆応用プログラムのソースに記述する方法

コールバック関数を静的に登録するには、応用プログラム中にSQLSetCallback関数を記述します。登録は各SQL文の種別ごとに行うため、登録するSQL文種別の数だけSQLSetCallback関数を実行します。また、複数のSQL文種別に対し同一のコールバック関数を登録する場合は、登録するSQL文種別のリストをSQLSetCallback関数に指定することで、一括して登録することができます。

SQLSetCallback関数は応用プログラム中のどこでも記述することができますが、コールバック関数を実行したいSQL文より前に記述する必要があります。

一度登録したSQL文種別に対し再度コールバック関数を登録すると、コールバック関数を変更することができます。また、登録したコールバック関数を解除したい場合は、SQLSetCallback関数の関数アドレスを指定するパラメタにNULLを指定してください。コールバック関数の変更および解除は、応用プログラム中のどの位置でも可能です。

マルチスレッド環境で動作する応用プログラムの場合、コールバック関数はセションごとにSQL文種別に登録されます。そのため、SQLSetCallback関数のパラメタにはコールバック関数を登録するセションIDを指定してください。また、SQLSetCallback関数を実行できるのは、SQLThrAllocID関数によるセション作成後になります。

コールバック関数を登録するSQL文の種別は、リストの形式で指定します。リストはshort型の配列変数の各要素に、登録したいSQL文の種別を設定して作成します。また、0が設定されている要素までをリストとみなすため、リストの最後には必ず0を設定してください。これは1種類のSQL文に対し登録する場合でも同様に設定する必要があります。

一度に設定できるSQL文の種別に制限はありません。また、重複して指定された場合もエラーにはなりません。

一度に複数のSQL文種別に対し登録した場合でも、再登録や解除は、個々または別の組合せで行うことが可能です。

 

/* INSERT文、UPDATE文にコールバック関数を登録する場合 */
SQLRETURN CallbackSQL1( ・・・ );
SQLRETURN CallbackSQL2( ・・・ );
SQLRETURN CallbackSQL3( ・・・ );
                                ・
                                ・
short       list[3];
                                ・
                                ・
/* 一括して登録する場合 */
list[0] = SQLRDB_CF_INSERT;
list[1] = SQLRDB_CF_UPDATE;
list[2] = 0;
SQLSetCallback( 0, CallbackSQL1, NULL, list, RDB_CALL_IN );
                                ・
/* 個別に登録する場合 */
list[0] = SQLRDB_CF_INSERT;
list[1] = 0;
SQLSetCallback( 0, CallbackSQL2, NULL, list, RDB_CALL_IN );
list[0] = SQLRDB_CF_UPDATE;
list[1] = 0;
SQLSetCallback( 0, CallbackSQL3, NULL, list, RDB_CALL_IN );
                                ・
                                ・

各SQL文の種別に対し現在コールバック関数が登録されているかどうかは、SQLGetCallback関数を使用することで取得できます。SQLGetCallback関数は、コールバック関数が登録されている場合はSQLRDB_ENTRYを返し、登録されていない場合はSQLRDB_NOENTRYを返します。

◆動的ライブラリを使用して登録する方

コールバック関数を動的に登録するためには、SQLDynSetCallback関数を含む登録用の動的ライブラリを作成します。動的ライブラリには、以下のSymfowareのライブラリをリンクしてください。

SQLDynSetCallback関数は利用者が作成する関数ですが、関数名、復帰値およびパラメタは規定されています。利用者はこの関数内で、SQLSetCallback関数を使用してコールバック関数を登録します。

SQLDynSetCallback関数が正常終了する場合は、復帰値としてSQLRDB_NORMALを返してください。しかし、エラーなどで関数が異常終了する場合は、SQLRDB_NORMAL以外を返却してください。

SQLDynSetCallback関数がエラーを返すとコールバック関数を利用する応用プログラムに以下のエラーが返ります。

動的ライブラリは、環境変数RDBSETCALLBACKまたはクライアント用の動作環境ファイルのSET_CALLBACKパラメタに指定します。環境変数RDBSETCALLBACKは、応用プログラムを実行する前に指定してください。

[UNIX系の環境変数の設定]

[Windowsの環境変数の設定]

set RDBSETCALLBACK = ライブラリ名

[動作環境ファイルのパラメタの指定]

SET_CALLBACK = (ライブラリ名)

環境変数または動作環境ファイルに設定するライブラリ名が絶対パスでの指定でない場合は、以下のディレクトリにライブラリを格納する必要があります。

環境変数RDBSETCALLBACKと動作環境ファイルのパラメタSET_CALLBACKの両方が指定された場合、環境変数RDBSETCALLBACKに指定された値が有効になります。

応用プログラムが実行されると、プロセス内で最初に実行されるSQL文の処理において、環境変数およびクライアント用の動作環境ファイルがチェックされ、動的ライブラリ内のSQLDynSetCallback関数が実行されます。なお、プロセス内の最初のSQL文に対しコールバック関数が指定されている場合もコールバック関数は実行されます。

image

なお、マルチスレッド環境で動作する応用プログラムの場合、SQLDynSetCallback関数はSQLThrAllocID関数の処理で実行されます。

image

■コールバック機能の使用

コールバック機能の使用例を以下に示します。

SQL文の実行時間が5秒以上であった場合、または、SQL文の実行エラーが発生した場合に、出口コールバック関数を使用して、標準出力に情報を出力します。

image

上記のコールバック関数と実行結果を以下に示します。

[UNIX系の場合]

コールバック関数 dynscb.c

/* libdynscb.so */
#include<stdio.h>
#include<sys/time.h>
#include"sqlrdbei.h"

/* コールバック関数の動的登録関数宣言 */
/* コールバック関数のプロトタイプ */
SQLRETURN CFEntry1( SQLHDBS, char *, char *, void *, SQLCALL_T * );
SQLRETURN CFEntry2( SQLHDBS, char *, char *, void *, SQLCALL_T * );

struct  timeval start_tv,end_tv;

/* コールバック関数の動的登録 */
SQLRETURN SQLDynSetCallback( SQLHDBS sid )
{
    SQLRETURN   ret;
    short       kind_list[14];

    /* コールバック関数の登録 */
    kind_list[0]  = (short)SQLRDB_CF_SELECT;
    kind_list[1]  = (short)SQLRDB_CF_DELETE_SEARCH;
    kind_list[2]  = (short)SQLRDB_CF_UPDATE_SEARCH;
    kind_list[3]  = (short)SQLRDB_CF_DELETE_POSITION;
    kind_list[4]  = (short)SQLRDB_CF_UPDATE_POSITION;
    kind_list[5]  = (short)SQLRDB_CF_INSERT;
    kind_list[6]  = (short)SQLRDB_CF_OPEN;
    kind_list[7]  = (short)SQLRDB_CF_FETCH;
    kind_list[8]  = (short)SQLRDB_CF_CLOSE;
    kind_list[9]  = (short)SQLRDB_CF_CALL;
    kind_list[10] = (short)SQLRDB_CF_COMMIT;
    kind_list[11] = (short)SQLRDB_CF_CONNECT;
    kind_list[12] = (short)SQLRDB_CF_DISCONNECT;
    kind_list[13] = 0;

    ret = SQLSetCallback(sid,CFEntry1,NULL,kind_list,SQLRDB_CALL_IN);
    if( ret != SQLRDB_NORMAL ){
        return( ret );
    }
    ret = SQLSetCallback(sid,CFEntry2,NULL,kind_list,SQLRDB_CALL_OUT);
    if( ret != SQLRDB_NORMAL){
        return( ret );
    }

    return( SQLRDB_NORMAL);
}

/* 入口コールバック関数 CFEntry1 */
SQLRETURN CFEntry1(SQLHDBS sid,
                   char    *SQLSTATE,
                   char    *SQLMSG,
                   void    *user_area,
                   SQLCALL_T *SQLDATA)
{
    /* 開始時刻の取得 */
    gettimeofday(&start_tv,NULL);
    return(SQLRDB_CONTINUE);
}

/* 出口コールバック関数 CFEntry2 */
SQLRETURN CFEntry2(SQLHDBS sid,
                   char    *SQLSTATE,
                   char    *SQLMSG,
                   void    *user_area,
                   SQLCALL_T *SQLDATA)
{
    long    exec_sec,exec_usec;

    /* 終了時刻の取得 */
    gettimeofday(&end_tv,NULL);
    /* 実行時間の計算 */
    exec_sec  = end_tv.tv_sec - start_tv.tv_sec;
    exec_usec = end_tv.tv_usec - start_tv.tv_usec;
    if(exec_usec<0){
        exec_sec--;
        exec_usec += 1000000;
    }
    /* エラーが発生した場合 */
    if((memcmp(SQLSTATE,"00",2)!=0) &&
       (memcmp(SQLSTATE,"01",2)!=0) &&
       (memcmp(SQLSTATE,"02",2)!=0)){
        printf("<<ERROR>>\n");
        printf("APPLICATION: %s\n" , SQLDATA->application);
        printf("LINE       : %ld\n", SQLDATA->sql_line);
        printf("SQL        : %s\n" , SQLDATA->sql_stmt);
        if(SQLMSG != NULL){
            printf("SQLSTATE   : %s\n" , SQLSTATE);
            printf("SQLMSG     : %s\n" , SQLMSG);
        }
        printf("EXEC TIME  : %ld.%06ld\n" , exec_sec , exec_usec);
        printf("\n");
    }
    /* 実行時間が5秒以上であった場合 */
    else if(exec_sec >= 5){
        printf("<<TIME-OVER>>\n");
        printf("APPLICATION: %s\n" , SQLDATA->application);
        printf("LINE       : %ld\n", SQLDATA->sql_line);
        printf("SQL        : %s\n" , SQLDATA->sql_stmt);
        printf("EXEC TIME  : %ld.%06ld\n" , exec_sec , exec_usec);
        printf("\n");
    }
    return(SQLRDB_CONTINUE);
}

実行結果

<<ERROR>>
APPLICATION: sample
LINE       : 17
SQL        : UPDATE SCHEMA1.TABLE1 SET COL01=1,COL02=2 WHERE COL01=1
SQLSTATE   : 40001
SQLMSG     : JYP2009E デッドロックが発生しました.
EXEC_TIME  : 0.000121

<<TIME-OVER>>
APPLICATION: sample
LINE       : 23
SQL        : DELETE FROM SCHEMA1.TABLE1 WHERE COL01<10
EXEC_TIME  : 6.000239

[Windowsの場合]

コールバック関数 dynscb.c

/* dynscb.dll */
#include<stdio.h>
#include<windows.h>
#include"sqlrdbei.h"

/* コールバック関数の動的登録関数宣言 */
/* コールバック関数のプロトタイプ */
SQLRETURN CFEntry1( SQLHDBS, char *, char *, void *, SQLCALL_T *);
SQLRETURN CFEntry2( SQLHDBS, char *, char *, void *, SQLCALL_T *);

unsigned long start_tv,end_tv;

/* コールバック関数の動的登録 */
SQLRETURN SQLDynSetCallback( SQLHDBS sid )
{
    SQLRETURN   ret;
    short   kind_list[14];

    /* コールバック関数の登録 */
    kind_list[0]  = (short)SQLRDB_CF_SELECT;
    kind_list[1]  = (short)SQLRDB_CF_DELETE_SEARCH;
    kind_list[2]  = (short)SQLRDB_CF_UPDATE_SEARCH;
    kind_list[3]  = (short)SQLRDB_CF_DELETE_POSITION;
    kind_list[4]  = (short)SQLRDB_CF_UPDATE_POSITION;
    kind_list[5]  = (short)SQLRDB_CF_INSERT;
    kind_list[6]  = (short)SQLRDB_CF_OPEN;
    kind_list[7]  = (short)SQLRDB_CF_FETCH;
    kind_list[8]  = (short)SQLRDB_CF_CLOSE;
    kind_list[9]  = (short)SQLRDB_CF_CALL;
    kind_list[10] = (short)SQLRDB_CF_COMMIT;
    kind_list[11] = (short)SQLRDB_CF_CONNECT;
    kind_list[12] = (short)SQLRDB_CF_DISCONNECT;
    kind_list[13] = 0;

    ret = SQLSetCallback(sid,CFEntry1,NULL,kind_list,SQLRDB_CALL_IN);
    if( ret != SQLRDB_NORMAL){
        return( ret );
    }
    ret = SQLSetCallback(sid,CFEntry2,NULL,kind_list,SQLRDB_CALL_OUT);
    if( ret != SQLRDB_NORMAL){
        return( ret );
    }

    return( SQLRDB_NORMAL );
}

/* 入口コールバック関数 CFEntry1 */
SQLRETURN CFEntry1(SQLHDBS sid,
           char    *SQLSTATE,
           char    *SQLMSG,
                   void    *user_area,
           SQLCALL_T *SQLDATA)
{
    /* 開始時刻の取得 */
    start_tv = GetTickCount();
    return(SQLRDB_CONTINUE);
}

/* 出口コールバック関数 CFEntry2 */
SQLRETURN CFEntry2(SQLHDBS sid,
           char    *SQLSTATE,
           char    *SQLMSG,
           void    *user_area,
           SQLCALL_T *SQLDATA)
{
    long exec_sec,exec_msec;

    /* 終了時刻の取得 */
    end_tv = GetTickCount();
    /* 実行時間の計算 */
    exec_sec = (end_tv-start_tv)/1000;
    exec_msec= (end_tv-start_tv)%1000;

    /* エラーが発生した場合 */
    if((memcmp(SQLSTATE,"00",2)!=0) &&
       (memcmp(SQLSTATE,"01",2)!=0) &&
       (memcmp(SQLSTATE,"02",2)!=0)){
       printf("<<ERROR>>\n");
       printf("APPLICATION:%s\n"  , SQLDATA->application);
       printf("LINE       :%ld\n" , SQLDATA->sql_line);
       printf("SQL        :%s\n"  , SQLDATA->sql_stmt);
           if(SQLMSG != NULL){
           printf("SQLSTATE  : %s\n" , SQLSTATE);
           printf("SQLMSG    : %s\n" , SQLMSG);
       }
       printf("EXEC TIME  :%ld.%03ld\n" , exec_sec , exec_msec);
       printf("\n");
    }
    /* 実行時間が5秒以上かかった場合 */
    else if(exec_sec >=5){
       printf("<<TIME-OVER>>\n");
       printf("APPLICATION: %s\n" , SQLDATA->application);
       printf("LINE       : %ld\n", SQLDATA->sql_line);
       printf("SQL        : %s\n" , SQLDATA->sql_stmt);
       printf("EXEC TIME  : %ld.%03ld\n" , exec_sec , exec_msec);
       printf("\n");
    }
    return(SQLRDB_CONTINUE);
}

実行結果

<<ERROR>>
APPLICATION: sample
LINE       : 17
SQL        : UPDATE SCHEMA1.TABLE1 SET COL01=1,COL02=2 WHERE COL01=1
SQLSTATE   : 40001
SQLMSG     : JYP2009E デッドロックが発生しました.
EXEC_TIME  : 0.078

<<TIME-OVER>>
APPLICATION: sample
LINE       : 23
SQL        : DELETE FROM SCHEMA1.TABLE1 WHERE COL01<10;
EXEC_TIME  : 13.125

目次 索引 前ページ次ページ

All Rights Reserved, Copyright (C) 富士通株式会社 2003-2004