ページの先頭行へ戻る
Symfoware Server V10.0.0/V10.0.1 アプリケーション開発ガイド(埋込みSQL編)

5.2.2 ライブラリの作成

Symfoware/RDBでは、ファンクションルーチンは外部ルーチンとして提供しています。したがって、利用者が作成したアプリケーションは、動的リンクライブラリとしてサーバに登録します。

以下に、ファンクションルーチンのライブラリの作成手順について説明します。

5.2.2.1 ライブラリとして登録するCプログラムの作成方法

ここでは、ファンクションルーチンのライブラリとして利用者が登録するCプログラムの作成方法について、以下を説明します。

Cプログラムの関数インタフェース

Cプログラムの関数の引数は、以下の順に2つの引数を固定で指定します。

  1. 引数データのポインタリスト

  2. 結果データのポインタリスト

例1

ファンクションルーチンのパラメタが3個の場合の関数宣言の例

/* 引数データのポインタリスト */
typedef struct in_ptr_list_tag {
   void  *ptr[6];                   /*ファンクションのパラメタの個数×2*/
} in_ptr_list_t;

/* 結果データのポインタリスト */
typedef struct out_ptr_list_tag {
   void  *ptr[2];                   /*返却データと標識データの格納先*/
} out_ptr_list_t;

void  UserFunc001(in_ptr_list_t  *in_p,    /* 引数データのポインタリスト */
                  out_ptr_list_t *out_p)   /* 結果データのポインタリスト */
{
  ・・・

例2

ファンクションルーチンのパラメタが10個の場合の関数宣言の例

/* 引数データのポインタリスト */
typedef struct in_ptr_list_tag {
   void  *ptr[20];                 /* ファンクションのパラメタの個数×2 */
} in_ptr_list_t;

/* 結果データのポインタリスト */
typedef struct out_ptr_list_tag {
   void  *ptr[2];                     /* 返却データと標識データの格納先 */
} out_ptr_list_t;

void  UserFunc002(in_ptr_list_t  *in_p,   /* 引数データのポインタリスト */
                  out_ptr_list_t *out_p)  /* 結果データのポインタリスト */
{
  ・・・
引数データのポインタリスト

ファンクションルーチンの入力パラメタをCプログラムの関数が受け取るには、引数データのポインタリストを利用します。引数データのポインタリストは、CREATE FUNCTION文のパラメタ宣言順に、入力データの格納先アドレスおよび対応する標識データの格納先アドレスが並びます。

入力データがNULL値でない場合は、標識データに0が通知されます。入力データがNULL値の場合は、標識データに負の値が通知され、入力データの値は保証されません。

入力データは、ファンクションルーチンのパラメタ宣言で指定したデータ型と対応するC言語のデータ型で表現されます。また、標識データは2バイト整数型で表現されます。

参照

ファンクションルーチンのパラメタのデータ型と対応するデータ形式については、“アプリケーション開発ガイド(共通編)”を参照してください。

結果データのポインタリスト

ファンクションルーチンの結果としてCプログラムの関数から返却する結果データは、結果データのポインタリストを利用します。結果データのポインタリストには、結果データの格納先アドレスと結果の標識データの格納先アドレスが設定されています。

Cプログラムの関数は、処理終了までに結果データを指定先の領域に格納する必要があります。結果データは、CREATE FUNCTION文で指定した戻りデータ型に対応した形式で設定してください。標識データは、2バイト整数型で0を設定してください。結果をNULL値として返却する場合は、標識データに負の値を設定してください。標識データが負の値の場合は、結果データは返却されません。

参照

パラメタのデータ型とC言語のデータ型の対応については、“アプリケーション開発ガイド(共通編)”の“ファンクションルーチンのパラメタのデータ型と対応するデータ形式”を参照してください。

パラメタのデータ型とC言語のデータ型の対応については、“ファンクションルーチンのパラメタのデータ型と対応するデータ形式”を参照してください。

ファンクションルーチンのパラメタとCプログラムの関数の引数のインタフェースについて、具体的な使用例を以下に示します。

SolarisLinux

ファンクションルーチン“USERFUNC001”の定義文

CREATE FUNCTION SCM1.USERFUNC001(IN CHAR(20),
                                 IN INTEGER,
                                 IN INTEGER )
  RETURNS VARCHAR(60)
  LANGUAGE C
  NAME 'UserFunc001'
  LIBRARY '/usr/local/lib/libuserfunc001.so';

ファンクションルーチン“USERFUNC001”のCプログラム(プログラム名:userfunc001.c)の作成例

/* compile: cc -G -o libuserfunc001.so userfunc001.c */
/* DEBUG  : cc -G -o libuserfunc001.so userfunc001.c -DDEBUG */
#include <stdio.h>
#include <string.h>

/* 引数データのポインタリスト */
typedef struct in_ptr_list_tag {
   void  *ptr[6];    /* ファンクションのパラメタの個数×2 */
} in_ptr_list_t;

/* 結果データのポインタリスト */
typedef struct out_ptr_list_tag {
   void  *ptr[2];    /* 返却データと標識データの格納先 */
} out_ptr_list_t;

extern void UserFunc001(in_ptr_list_t  *in_p,
                        out_ptr_list_t *out_p)
{
    char    *c1_data_p, *ret_data_p;
    int     *c2_data_p, *c3_data_p;
    short   *c1_ind_p, *c2_ind_p, *c3_ind_p, *ret_ind_p;
    char    data1[21], ret[61];
    int     data2, data3;
    short   ind1, ind2, ind3, ret_size;
#ifdef DEBUG
    FILE    *fp;
#endif

#ifdef DEBUG
    fp = fopen("/usr/local/work/func001.log", "w");
    if (fp == NULL) return;
    fprintf(fp, "UserFunc001: start\n"); fflush(fp);
#endif
    /* 入力情報 */
    c1_data_p = (char  *)in_p->ptr[0];   /* 引数1のデータ     */
    c1_ind_p  = (short *)in_p->ptr[1];   /* 引数1の標識データ */
    c2_data_p = (int   *)in_p->ptr[2];   /* 引数2のデータ     */
    c2_ind_p  = (short *)in_p->ptr[3];   /* 引数2の標識データ */
    c3_data_p = (int   *)in_p->ptr[4];   /* 引数3のデータ     */
    c3_ind_p  = (short *)in_p->ptr[5];   /* 引数3の標識データ */
    /* 出力情報 */
    ret_data_p = (char *)out_p->ptr[0];  /* 結果のデータ格納先 */
    ret_ind_p  = (short *)out_p->ptr[1]; /* 結果の標識データ〃 */

    /* 入力データを自動変数に取り出す */
    memcpy(data1, c1_data_p, 20);
    /* 文字列として扱うため終端にNULL文字設定 */
    data1[20] = '\0';
    data2 = *c2_data_p;
    data3 = *c3_data_p;
    ind1  = *c1_ind_p;
    ind2  = *c2_ind_p;
    ind3  = *c3_ind_p;

#ifdef DEBUG
    fprintf(fp, "UserFunc001: para1=%s, para2=%ld, para3=%ld\n",
            data1, data2, data3);
    fflush(fp);
#endif

    /* 結果データの作成 */
    sprintf(ret, "NAME=%20s, CODE=%ld, AGE=%ld", data1, data2,
            data3);
    ret_size = strlen(ret);
#ifdef DEBUG
    fprintf(fp, "UserFunc001: ret=%s\n", ret); fflush(fp);
#endif
    /* 指定された結果データの格納先に結果データを書き込む */
    memcpy(ret_data_p, &ret_size, sizeof(short));
    memcpy((ret_data_p + sizeof(short)), ret, ret_size);
    *ret_ind_p = 0;

#ifdef DEBUG
    fprintf(fp, "UserFunc001: end\n"); fflush(fp);
    fclose(fp);
#endif
    return;
}

上記の例では、#ifdef DEBUG~#endif間にデバッグ用の処理を追加しています。後述のテストドライバと組み合わせることでSymfoware/RDBを介さない単体テストを行うことができます。ファンクションルーチンではfprintf関数やいくつかの関数の使用を制限していますが、単体テストであれば、上記のように使用することができます。コンパイル時にデファイン値DEBUGを指定してください。Symfoware/RDBを介して実行するときは、DEBUGを外して再コンパイルしてください。

Windows

ファンクションルーチン“USERFUNC001”の定義文

CREATE FUNCTION SCM1.USERFUNC001(IN CHAR(20),
                                 IN INTEGER,
                                 IN INTEGER )
  RETURNS VARCHAR(60)
  LANGUAGE C
  NAME 'UserFunc001'
  LIBRARY 'D:\FORSYMFO\FUNCLIB\USERFUNC001.DLL';

ファンクションルーチン“USERFUNC001”のCプログラムの作成例

#include <stdio.h>
#include <string.h>

/* 引数データのポインタリスト */
typedef struct in_ptr_list_tag {
   void  *ptr[6];    /* ファンクションのパラメタの個数×2 */
} in_ptr_list_t;

/* 結果データのポインタリスト */
typedef struct out_ptr_list_tag {
   void  *ptr[2];    /* 返却データと標識データの格納先 */
} out_ptr_list_t;

extern __declspec(dllexport)
void UserFunc001(in_ptr_list_t  *in_p,
                 out_ptr_list_t *out_p)
{
    char    *c1_data_p, *ret_data_p;
    int     *c2_data_p, *c3_data_p;
    short   *c1_ind_p, *c2_ind_p, *c3_ind_p, *ret_ind_p;
    char    data1[21], ret[61];
    int     data2, data3;
    short   ind1, ind2, ind3, ret_size;
#ifdef DEBUG
    FILE    *fp;
#endif

#ifdef DEBUG
    fp = fopen("D:\\FORSYMFO\\WORK\\FUNC001.LOG", "w");
    if (fp == NULL) return;
    fprintf(fp, "UserFunc001: start\n"); fflush(fp);
#endif
    /* 入力情報 */
    c1_data_p = (char  *)in_p->ptr[0];    /* 引数1のデータ     */
    c1_ind_p  = (short *)in_p->ptr[1];    /* 引数1の標識データ */
    c2_data_p = (int   *)in_p->ptr[2];    /* 引数2のデータ     */
    c2_ind_p  = (short *)in_p->ptr[3];    /* 引数2の標識データ */
    c3_data_p = (int   *)in_p->ptr[4];    /* 引数3のデータ     */
    c3_ind_p  = (short *)in_p->ptr[5];    /* 引数3の標識データ */
    /* 出力情報 */
    ret_data_p = (char *)out_p->ptr[0];   /* 結果のデータ格納先 */
    ret_ind_p  = (short *)out_p->ptr[1];  /* 結果の標識データ〃 */

    /* 入力データを自動変数に取り出す */
    memcpy(data1, c1_data_p, 20);
    /* 文字列として扱うため終端にNULL文字設定 */
    data1[20] = '\0';
    data2 = *c2_data_p;
    data3 = *c3_data_p;
    ind1  = *c1_ind_p;
    ind2  = *c2_ind_p;
    ind3  = *c3_ind_p;
#ifdef DEBUG
    fprintf(fp, "UserFunc001: para1=%s, para2=%ld, para3=%ld\n",
            data1, data2, data3);
    fflush(fp);
#endif

    /* 結果データの作成 */
    sprintf(ret, "NAME=%20s, CODE=%ld, AGE=%ld", data1, data2,
            data3);
    ret_size = strlen(ret);
#ifdef DEBUG
    fprintf(fp, "UserFunc001: ret=%s\n", ret); fflush(fp);
#endif
    /* 指定された結果データの格納先に結果データを書き込む */
    memcpy(ret_data_p, &ret_size, sizeof(short));
    memcpy((ret_data_p + sizeof(short)), ret, ret_size);
    *ret_ind_p = 0;

#ifdef DEBUG
    fprintf(fp, "UserFunc001: end\n"); fflush(fp);
    fclose(fp);
#endif
    return;
}

上記の例では、#ifdef DEBUG~#endif間にデバッグ用の処理を追加しています。後述のテストドライバと組み合わせることでSymfoware/RDBを介さない単体テストを行うことができます。ファンクションルーチンではfprintf関数やいくつかの関数の使用を制限していますが、単体テストであれば、上記のように使用することができます。コンパイル時にデファイン値DEBUGを指定してください。Symfoware/RDBを介して実行するときは、DEBUGを外して再コンパイルしてください。

ファンクションルーチンのライブラリで使用可能な標準関数

ファンクションルーチンのライブラリで使用可能なC言語の標準関数について、以下に示します。使用不可の関数を使用した際の動作は保証されません。

また、プログラム中にSQL文を記述することはできません。

表5.1 使用可能なC言語の標準関数

関数の種別

インクルードファイル

関数名

使用の可否

文字列関数

string.h

strcat,strncat,strcmp,strncmp,strcpy,strncpy,strlen

stdlib.h

atof,atoi,atol

算術関数

stdlib.h

abs,rand,srand

math.h

acos,asin,atan,cos,exp,log,sin,tan,sqrt

メモリ制御
関数

stdlib.h

malloc,free,realloc

時間関数

time.h

asctime,ctime,gmtime,localtime,time

標準入出力
関数

stdio.h

sprintf

fgets,gets,fputs,puts,fgetc,getc,fputc,putc,
printf,fprintf,scanf,fscanf,sscanf,fflush,
fopen,fwrite,fread,fclose,fseek,feof,ferror

×

低水準入出力
関数

fcntl.h

open,creat

×

unistd.h

close,lseek,read,write

×

ファイル操作
関数

unistd.h

chdir,rmdir

×

stdio.h

rename

×

プロセス制御
関数

stdlib.h

abort

×

unistd.h

fork,execl,execv,execle,execve,execvp

×

signal.h

signal

×

wait.h

wait

×

その他の関数

*.h

getenv,putenv, ...

×

○:使用可能

×:使用不可

5.2.2.2 ライブラリとして登録するCプログラムのコンパイル・リンク

ファンクションルーチンのライブラリは、動的リンクライブラリとして作成します。

Solaris動的モードを指定して共有オブジェクトを作成します。

  cc  -KPIC -G  -o  <ライブラリ名>  <ソースファイル名>...

作成したライブラリは、ファンクションルーチン定義文のLIBRARY句で指定するディレクトリに格納してください。

  cc -KPIC -G -o /usr/local/lib/libuserfunc001.so  userfunc001.c

SQLSignalMSG関数を使用する場合は、ライブラリlibsqlsigm.soを動的結合する必要があります。また、インクルードファイルsqlsigm.hをインクルードする必要があります。


  cc -KPIC -G -o /usr/local/lib/libuserfunc001.so
   -L/opt/FSUNrdb2b/lib  -lsqlsigm  -I/opt/FSUNrdb2b/include 
        (1)                  (2)              (3)
   userfunc001.c

(1) RDBライブラリパス指定

(2) RDBライブラリ名

(3) RDBインクルードパス指定

Linux動的モードを指定して共有オブジェクトを作成します。

  gcc  -fPIC -shared  -o  <ライブラリ名>  <ソースファイル名>...

作成したライブラリは、ファンクションルーチン定義文のLIBRARY句で指定するディレクトリに格納してください。

  gcc -fPIC -shared -o /usr/local/lib/libuserfunc001.so  userfunc001.c

SQLSignalMSG関数を使用する場合は、ライブラリlibsqlsigm.soを動的結合する必要があります。また、インクルードファイルsqlsigm.hをインクルードする必要があります。


  gcc -fPIC -shared -o /usr/local/lib/libuserfunc001.so
     -L/opt/FJSVrdb2b/lib  -lsqlsigm  -I/opt/FJSVrdb2b/include 
           (1)                  (2)              (3)
     userfunc001.c

(1) RDBライブラリパス指定

(2) RDBライブラリ名

(3) RDBインクルードパス指定

WindowsDynamic-Link Library(DLL)を作成します。
DLLの対象プラットフォームは、サーバが動作するプラットフォームと同じにします。x64版の場合はx64、それ以外の場合はWin32を対象プラットフォームとしてDLLを作成します。

Microsoft(R) Visual Studioでの作成手順の例を以下に示します。

  1. [ファイル(F)]メニューから、[新規作成(N)]で[プロジェクト(P)]を選択します。

  2. [新しいプロジェクト]ウインドウにて、[プロジェクトの種類(P)]で[Visual C++]の[Win32]を選択し、[テンプレート(T)]で[Win32 プロジェクト]を選択します。

  3. [新しいプロジェクト]ウインドウにて、[プロジェクト名(N)]を指定し、[場所(L)]にライブラリ作成場所を指定します。[ソリューションのディレクトリを作成(D)]のチェックボックスのチェックを外します。[OK]を選択します。

    プロジェクト名 = USERFUNC001
    位置 = D:\FORSYMFO\FUNCMAKE\USERFUNC001

    指定した位置に、作成したCプログラムファイルを格納しておきます。

    ファンクションルーチンのライブラリ内でSQLSignalMSG関数を使用する場合は、インクルードファイルsqlsigm.hをインクルードする必要があります。このインクルードファイルは、<サーバ機能のインストール先>\ESQL\INCLUDEまたは<クライアント機能のインストール先>\ESQL\INCLUDEに格納されています。

  4. [Win32 アプリケーション ウィザード]ウインドウの[Win32 アプリケーション ウィザードへようこそ]の画面にて[次へ>]を選択します。

  5. [Win32 アプリケーション ウィザード]ウインドウの[アプリケーションの設定]の画面にて、[アプリケーションの種類]で[DLL]を選択します。[追加オプション]で[空のプロジェクト(E) ]のチェックボックスをチェックします。[完了]を選択します。

  6. x64版の場合、事前にVisual Studioのプロジェクト設定で64ビットのプロジェクトを作成しておく必要があります。
    [ビルド(B)]の[構成マネージャ(O)...]から[構成マネージャ]ウィンドウを開きます。
    [プロジェクトのコンテキスト (ビルドまたは配置するプロジェクト構成をチェック)(R):]の[プラットフォーム]のプルダウンから<新規作成...>を選択します。
    [新しいプロジェクト プラットフォーム]ウィンドウを開きます。
    [新しいプラットフォーム(P):]のプルダウンから“x64”を選択し、[OK]をクリックします。
    その際、[構成マネージャ]の[アクティブ ソリューション プラットフォーム(P):]のプルダウンと、[プロジェクトのコンテキスト (ビルドまたは配置するプロジェクト構成をチェック)(R):]の[プラットフォーム]で、目的のプラットフォームが選択されていることを確認します。
    確認ができたら[構成マネージャ]の[閉じる]をクリックします。

  7. [プロジェクト(P)]の、[既存項目の追加(G)]から、作成したCプログラムファイルをプロジェクトに追加します。

    D:\FORSYMFO\FUNCMAKE\USERFUNC001\USERFUNC001.C
  8. ファンクションルーチンのライブラリ内でSQLSignalMSG関数を使用する場合は、インクルードディレクトリにSymfoware ServerのINCLUDEディレクトリを追加し、サーバのプラットフォームに対応した以下のライブラリをリンクする必要があります。

    • x64版の場合
      リンクするライブラリ … f3cwsqlsigm_x64.lib

    • 上記以外の場合
      リンクするライブラリ … f3cwsqlsigm.lib

    1. インクルードパスを設定します。[プロジェクト(P)]の[プロパティ(P)] から[プロパティ]ウィンドウを開きます。[構成プロパティ]を選択し、[構成(C):]のプルダウンから“すべての構成”、[プラットフォーム(P):]のプルダウンから“すべてのプラットフォーム”を選択します。
      [構成プロパティ]の[C/C++]で[全般]の[追加のインクルードディレクトリ]に<サーバ機能のインストール先>\ESQL\INCLUDEまたは<クライアント機能のインストール先>\ESQL\INCLUDEを追加し、[OK]をクリックします。

    2. ライブラリを設定します。[プロジェクト(P)]の、[既存項目の追加(G)]から、使用するライブラリをプロジェクトに追加します。

  9. [ビルド(B)]から[ソリューションのビルド(B)]を選択して、ライブラリを作成します。

  10. 作成したライブラリを、ファンクションルーチン定義文のLIBRARY句で指定するディレクトリに格納します。

    D:\FORSYMFO\FUNCMAKE\USERFUNC001\RELEASE\USERFUNC001.DLL
                 ↓ 複写
    D:\FORSYMFO\FUNCLIB\USERFUNC001.DLL

Windows5.2.2.3 ライブラリを作成する場合の注意事項

ファンクションルーチンのライブラリとして作成するCプログラムの関数は、エクスポート関数にしておく必要があります。

エクスポート関数の宣言は、__declspec(dllexport) を指定します。

dllexportを指定した関数の宣言

/* 引数データのポインタリスト */
typedef struct in_ptr_list_tag {
   void  *ptr[6];                  /*ファンクションのパラメタの個数×2*/
} in_ptr_list_t;

/* 結果データのポインタリスト */
typedef struct out_ptr_list_tag {
   void  *ptr[2];                     /* 返却データと標識データの格納先 */
} out_ptr_list_t;

/* 関数宣言 */
extern __declspec(dllexport)
void UserFunc001(in_ptr_list_t *in_p, /* 引数データのポインタリスト */ out_ptr_list_t *out_p) /* 結果データのポインタリスト */ { ・・・