Symfoware/RDBでは、ファンクションルーチンは外部ルーチンとして提供しています。したがって、利用者が作成したアプリケーションは、動的リンクライブラリとしてサーバに登録します。
以下に、ファンクションルーチンのライブラリの作成手順について説明します。
ライブラリとして登録するCプログラムの作成方法
ライブラリとして登録するCプログラムのコンパイル・リンク
Windows(R) 2000、Windows Server(R) 2003およびWindows Server(R) 2008でライブラリを作成する場合の注意事項
ここでは、ファンクションルーチンのライブラリとして利用者が登録するCプログラムの作成方法について、以下を説明します。
Cプログラムの関数インタフェース
ファンクションルーチンのライブラリで使用可能な標準関数
Cプログラムの関数インタフェース
Cプログラムの関数の引数は、以下の順に2つの引数を固定で指定します。
引数データのポインタリスト
結果データのポインタリスト
ファンクションルーチンのパラメタが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) /* 結果データのポインタリスト */ { ・・・
ファンクションルーチンのパラメタが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言語のデータ型の対応については、“付録K ファンクションルーチンのパラメタのデータ型と対応するデータ形式”を参照してください。
ファンクションルーチンの結果としてCプログラムの関数から返却する結果データは、結果データのポインタリストを利用します。結果データのポインタリストには、結果データの格納先アドレスと結果の標識データの格納先アドレスが設定されています。
Cプログラムの関数は、処理終了までに結果データを指定先の領域に格納する必要があります。結果データは、CREATE FUNCTION文で指定した戻りデータ型に対応した形式で設定してください。標識データは、2バイト整数型で0を設定してください。結果をNULL値として返却する場合は、標識データに負の値を設定してください。標識データが負の値の場合は、結果データは返却されません。
パラメタのデータ型とC言語のデータ型の対応については、“付録K ファンクションルーチンのパラメタのデータ型と対応するデータ形式”を参照してください。
ファンクションルーチンのパラメタとCプログラムの関数の引数のインタフェースについて、具体的な使用例を以下に示します。
ファンクションルーチン“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を外して再コンパイルしてください。
ファンクションルーチン“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文を記述することはできません。
○:使用可能
×:使用不可
ファンクションルーチンのライブラリは、動的リンクライブラリとして作成します。
動的モードを指定して共有オブジェクトを作成します。
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インクルードパス指定
動的モードを指定して共有オブジェクトを作成します。
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インクルードパス指定
Dynamic-Link Library(DLL)を作成します。
DLLの対象プラットフォームは、サーバが動作するプラットフォームと同じにします。Itanium版64ビットEEまたはItanium版64ビットSEの場合はItanium、x64版64ビットEEまたはx64版64ビットSEの場合はx64、それ以外の場合はWin32を対象プラットフォームとしてDLLを作成します。
Microsoft(R) Visual Studioでの作成手順の例を以下に示します。
[ファイル(F)]メニューから、[新規作成(N)]で[プロジェクト(P)]を選択します。
[新しいプロジェクト]ウインドウにて、[プロジェクトの種類(P)]で[Visual C++]の[Win32]を選択し、[テンプレート(T)]で[Win32 プロジェクト]を選択します。
[新しいプロジェクト]ウインドウにて、[プロジェクト名(N)]を指定し、[場所(L)]にライブラリ作成場所を指定します。[ソリューションのディレクトリを作成(D)]のチェックボックスのチェックを外します。[OK]を選択します。
プロジェクト名 = USERFUNC001 位置 = D:\FORSYMFO\FUNCMAKE\USERFUNC001
指定した位置に、作成したCプログラムファイルを格納しておきます。
ファンクションルーチンのライブラリ内でSQLSignalMSG関数を使用する場合は、インクルードファイルsqlsigm.hをインクルードする必要があります。このインクルードファイルは、<サーバ機能のインストール先>\ESQL\INCLUDEまたは<クライアント機能のインストール先>\ESQL\INCLUDEに格納されています。
[Win32 アプリケーション ウィザード]ウインドウの[Win32 アプリケーション ウィザードへようこそ]の画面にて[次へ>]を選択します。
[Win32 アプリケーション ウィザード]ウインドウの[アプリケーションの設定]の画面にて、[アプリケーションの種類]で[DLL]を選択します。[追加オプション]で[空のプロジェクト(E) ]のチェックボックスをチェックします。[完了]を選択します。
Itanium版64ビットEE、Itanium版64ビットSE、x64版64ビットEEまたはx64版64ビットSEの場合、事前にVisual Studioのプロジェクト設定で64ビットのプロジェクトを作成しておく必要があります。
[ビルド(B)]の[構成マネージャ(O)...]から[構成マネージャ]ウィンドウを開きます。
[プロジェクトのコンテキスト (ビルドまたは配置するプロジェクト構成をチェック)(R):]の[プラットフォーム]のプルダウンから<新規作成...>を選択します。
[新しいプロジェクト プラットフォーム]ウィンドウを開きます。
Itanium版64ビットEEまたはItanium版64ビットSEの場合
[新しいプラットフォーム(P):]のプルダウンから“Itanium”を選択し、[OK]をクリックします。
x64版64ビットEEまたはx64版64ビットSEの場合
[新しいプラットフォーム(P):]のプルダウンから“x64”を選択し、[OK]をクリックします。
その際、[構成マネージャ]の[アクティブ ソリューション プラットフォーム(P):]のプルダウンと、[プロジェクトのコンテキスト (ビルドまたは配置するプロジェクト構成をチェック)(R):]の[プラットフォーム]で、目的のプラットフォームが選択されていることを確認します。
確認ができたら[構成マネージャ]の[閉じる]をクリックします。
[プロジェクト(P)]の、[既存項目の追加(G)]から、作成したCプログラムファイルをプロジェクトに追加します。
D:\FORSYMFO\FUNCMAKE\USERFUNC001\USERFUNC001.C
ファンクションルーチンのライブラリ内でSQLSignalMSG関数を使用する場合は、インクルードディレクトリにSymfoware ServerのINCLUDEディレクトリを追加し、サーバのプラットフォームに対応した以下のライブラリをリンクする必要があります。
Itanium版64ビットEEまたはItanium版64ビットSEの場合
リンクするライブラリ … f3cwsqlsigm_ia64.lib
x64版64ビットEEまたはx64版64ビットSEの場合
リンクするライブラリ … f3cwsqlsigm_x64.lib
上記以外の場合
リンクするライブラリ … f3cwsqlsigm.lib
インクルードパスを設定します。[プロジェクト(P)]の[プロパティ(P)] から[プロパティ]ウィンドウを開きます。[構成プロパティ]を選択し、[構成(C):]のプルダウンから“すべての構成”、[プラットフォーム(P):]のプルダウンから“すべてのプラットフォーム”を選択します。
[構成プロパティ]の[C/C++]で[全般]の[追加のインクルードディレクトリ]に<サーバ機能のインストール先>\ESQL\INCLUDEまたは<クライアント機能のインストール先>\ESQL\INCLUDEを追加し、[OK]をクリックします。
ライブラリを設定します。[プロジェクト(P)]の、[既存項目の追加(G)]から、使用するライブラリをプロジェクトに追加します。
[ビルド(B)]から[ソリューションのビルド(B)]を選択して、ライブラリを作成します。
作成したライブラリを、ファンクションルーチン定義文のLIBRARY句で指定するディレクトリに格納します。
D:\FORSYMFO\FUNCMAKE\USERFUNC001\RELEASE\USERFUNC001.DLL ↓ 複写 D:\FORSYMFO\FUNCLIB\USERFUNC001.DLL
ファンクションルーチンのライブラリとして作成する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) /* 結果データのポインタリスト */ { ・・・