MySQLに新しい関数を追加するには2つの方法があります:
CREATE FUNCTION と DROP FUNCTION ステー
トメントを使用して、動的に追加、削除されます。
「7.30 CREATE FUNCTION/DROP FUNCTION 構文」節参照.
mysqld サーバに統合され、恒久的に有効になります。
それぞれの方法には、有利な点と不利な点があります:
新しい関数を追加するためにどちらの方法を使用しても、ABS() や
SOUNDEX() のようなネイティブ関数と同じように使用することができま
す。
UDF 機構が動作するためには、関数は C か C++ で書かれる必要があり、OS が 動的ローディングをサポートする必要があります。MySQL ソース配布 は `sql/udf_example.cc' を含んでいて、これは5つの新しい関数を定義 しています。UDF の呼び出し方法がどのように働くかはこのファイルを参考にし てください。
SQL ステートメントで使用したいそれぞれの関数について、対応する C (または
C++) 関数を定義すべきです。下の説明では、名前 ``xxx'' がサンプル関数名と
して使用されています。SQL と C/C++ 使用法を distinquish するために、
XXX() (大文字) は SQL 関数呼び出しを表わし、xxx() (小文字)
は C/C++ 関数呼び出しを表わします。
XXX() のインタフェースを実装するために書く C/C++ 関数は:
xxx() (必要)
| SQL 型 | C/C++ 型 |
STRING | char *
|
INTEGER | long long
|
REAL | double
|
xxx_init() (オプション)
xxx() の初期化関数。これは次のように使用されます:
XXX() の引数の数のチェック
REAL 関数では) 小数部の最大桁数の指定
NULL になり得るかどうかの指定
xxx_deinit() (オプション)
xxx() の終了関数(deinitialization function)。これは初期化関数によっ
て割り当てられたメモリを解放すべきです。
SQL ステートメントが XXX() を呼び出すとき、MySQL は引数
チェックやメモリ割り当てのように、必要なセットアップを行なわせるために、
初期化関数 xxx_init() を呼び出します。xxx_init() がエラー
を返す場合、SQL ステートメントはエラーメッセージと共に異常終了し、メイン
関数と終了関数は呼び出されません。そうでなければ、メイン関数
xxx() が各行毎に呼び出されます。全ての行が処理された後、終了関数
xxx_deinit() が、必要な掃除を行なうために、呼び出されます。
全ての関数はスレッド安全でなくてはいけません(メイン関数だけでなく、初期
化関数と終了関数も同様です)。これは変更されうるグローバル変数や静的変数
を割り当てることは許されないことを意味します! メモリが必要な場合は、
xxx_init() で割り当て、xxx_deinit() でそれを解放すべきです。
メイン関数は下に示すように宣言されるべきです。戻り値型と引数は、
CREATE FUNCTION ステートメントで、SQL 関数 XXX() の戻り値
をSTRING, INTEGER, REAL のどれに宣言するかに依存し
て異なることに注意してください:
STRING 関数では:
char *xxx(UDF_INIT *initid, UDF_ARGS *args,
char *result, unsigned long *length,
char *is_null, char *error);
INTEGER 関数では:
long long xxx(UDF_INIT *initid, UDF_ARGS *args,
char *is_null, char *error);
REAL 関数では:
double xxx(UDF_INIT *initid, UDF_ARGS *args,
char *is_null, char *error);
初期化と終了関数は次のように宣言します:
my_bool xxx_init(UDF_INIT *initid, UDF_ARGS *args, char *message); void xxx_deinit(UDF_INIT *initid);
initid 引数は3つの関数全てに渡されます。これは UDF_INIT
構造体を示し、関数間で情報を伝達するために使用されます。UDF_INIT
構造体のメンバは下に一覧します。初期化関数は変更したい全てのメンバを埋め
るべきです。(メンバにデフォルトを使用するには、未変更のままにしてくださ
い。)
my_bool maybe_null
xxx() が NULL を返す場合、xxx_init() は
maybe_null を 1 に設定すべきです。デフォルト値は、引数のい
ずれかが maybe_null として宣言された場合、1 です。
unsigned int decimals
1.34, 1.345, 1.3 が渡された場合、
デフォルトは 3 です。1.345 が 3 桁の小数部を持つためです。)
unsigned int max_length
initid->decimals
で示される小数部の桁数を加えたものです。(数値関数では、長さは符号と小数
点文字を含みます。)
char *ptr
initid->ptr を使用できます。
xxx_init() では、メモリを獲得し、このポインタに割り当てます:
initid->ptr = allocated_memory;
xxx() と xxx_deinit() では、initid->ptr を参照して、
メモリを使用または解放します。
args 引数は次に示すメンバを持つ UDF_ARGS 構造体へのポイン
タです:
unsigned int arg_count
if (args->arg_count != 2)
{
strcpy(message,"XXX() requires two arguments");
return 1;
}
enum Item_result *arg_type
STRING_RESULT, INT_RESULT,
REAL_RESULT です。
引数が与えられた型であることと、そうでない場合エラーを返すことを確実にす
るために、初期化関数で arg_type 配列をチェックしてください。例え
ば:
if (args->arg_type[0] != STRING_RESULT
&& args->arg_type[1] != INT_RESULT)
{
strcpy(message,"XXX() requires a string and an integer");
return 1;
}
関数の引数が特定の型を要求するための代案として、初期化関数を使用して、
arg_type 要素を求める型に設定できます。これは MySQL に
xxx() の各呼び出しにこれらの型を引数に強制するようにさせます。例
えば、最初の2つの引数に文字列と整数を強制するように指定するには、
xxx_init() で次を行なってください:
args->arg_type[0] = STRING_RESULT; args->arg_type[1] = INT_RESULT;
char **args
args->args は、関数が呼ばれる時の引数の一般的な特性について、情報
を初期化関数に伝達します。定数引数 i について、
args->args[i] は引数値のをポイントします。(値への正しいアクセス方
法については後述。)非定数引数について、args->args[i] は 0
です。定数引数は定数だけを使用する表現で、3, 4*7-2,
SIN(3.14) などです。非定数引数は行から行に変更される値を参照する
表現で、項目名や非定数引数で呼び出される関数などです。
メイン関数のそれぞれの呼び出しについて、args->args は現在処理され
ている行に渡される実際の引数を含んでいます。
関数は次のように引数 i を参照できます:
STRING_RESULT 型の引数は、バイナリデータまたは任意の長さのデータ
の処理を許すため、文字列ポインタ+長さとして与えられます。文字列内容は
args->args[i] として有効で、文字列長は args->lengths[i] で
す。文字列は NULL 終端とみなすべきではありません。
INT_RESULT 型の引数について、args->args[i] を
long long 値にキャストする必要があります:
long long int_val; int_val = *((long long*) args->args[i]);
REAL_RESULT 型の引数について、args->args[i] を
double 値にキャストする必要があります:
double real_val; real_val = *((double*) args->args[i]);
unsigned long *lengths
lengths 配列は各引数についての最大文字列長を示し
ます。メイン関数の各呼び出しについては、lengths は、現在処理され
ている行に渡される任意の文字列引数の実際の長さを含んでいます。
INT_RESULT, REAL_RESULT 型の引数については、lengths
はまだ引数の最大長を含んでいます(初期化関数については)。
初期化関数は、エラーが無い場合は 0 を、そうでなければ 1 を
返すべきです。エラーが発生する場合は、xxx_init() は NULL 終端エラー
メッセージを message パラメータに格納すべきです。このメッセージは
クライアントに返されます。メッセージバッファは MYSQL_ERRMSG_SIZE
文字長ですが、80 文字より小さく保つように試みるべきです。標準的な端末画
面の幅にフィットするようにです。
メイン関数 xxx() の返す値は long long と double 関
数については関数値です。文字列関数については、result と
length 引数で文字列が返されます。result は少なくとも 255
バイト長のバッファです。これらに内容と戻り値の長さを設定してください。例
えば:
memcpy(result, "result string", 13); *length = 13;
文字列関数は通常は結果をポイントする値を返します。
メイン関数で NULL 値の戻り値を示すには、is_null を
1 に設定してください:
*is_null = 1;
メイン関数でエラーの戻り値を示すには、error パラメータを 1
に設定します:
*error = 1;
xxx() が任意の行について *error に 1 を設定する場合、
関数値は現在の行についてと、その後の XXX() が呼び出されるステート
メントによって処理される任意の行について NULL です。(xxx()
は続く行については呼び出しさえされません。) 注意: 3.22.10 より
前の MySQL バージョンでは、*error と *is_null の
両方に設定すべきです:
*error = 1; *is_null = 1;
UDF を実装するファイルはサーバが動作するホスト上でコンパイルされインストー ルされないければなりません。この処理はサンプル UDF ファイル `udf_example.cc' について次に説明します。これは MySQL ソー ス配布に含まれています。このファイルは次の関数を含みます:
metaphon() は文字列引数の metaphon 文字列を返します。これは時には
soundex 文字列ですが、さらに英語用に調整されています。
myfunc_double() は引数の文字の ASCII 値の合計を引数の長さの合計で
割った値を返します。
myfunc_int() は引数の長さの合計を返します。
lookup() はホスト名の IP 番号を返します。
reverse_lookup() は IP 番号のホスト名を返します。この関数は文字列
"xxx.xxx.xxx.xxx" または4つの数値とともに呼ばれます。
動的ロード可能ファイルは、次のような何らかのコマンドを使用して、共有オブ ジェクトファイルとしてコンパイルされるべきです:
shell> gcc -shared -o udf_example.so myfunc.cc
MySQL ソースツリーの `sql' ディレクトリ内で次のコマンドを 実行することにより、システムのコンパイラオプションを簡単に正しく見つけ出 すことができます:
shell> make udf_example.o
make が表示するものに似たコンパイルコマンドを実行すべきですが、行
末近くの -c オプションを取り除いて、-o udf_example.so を行
末につけてください。(いくつかのシステムでは、コマンドに -c を残す
必要があります。)
UDF を含む共有オブジェクトをコンパイルすると、それをインストールしてその
ことを MySQL に知らせる必要があります。`udf_example.cc' か
らの共有オブジェクトのコンパイルは、`udf_example.so' のような何かの
ファイル名を提供します(実際の名前はプラットフォームによって変わります)。
このファイルを `/usr/lib' のような ld が探すどこかのディレク
トリにコピーしてください。多くのシステムでは、LD_LIBRARY または
LD_LIBRARY_PATH 環境変数を設定して、UDF 関数ファイルがあるディレ
クトリを示すことができます。dopen マニュアルページはシステム上で
使用すべき変数を教えてくれます。これを mysql.server または
safe_mysqld に設定し、mysqld を再起動すべきです。
ライブラリがインストールされた後、mysqld に新しい関数について次の
コマンドで通知すべきです:
mysql> CREATE FUNCTION metaphon RETURNS STRING SONAME "udf_example.so"; mysql> CREATE FUNCTION myfunc_double RETURNS REAL SONAME "udf_example.so"; mysql> CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "udf_example.so"; mysql> CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so"; mysql> CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so";
関数は DROP FUNCTION を使用して削除できます:
mysql> DROP FUNCTION metaphon; mysql> DROP FUNCTION myfunc_double; mysql> DROP FUNCTION myfunc_int; mysql> DROP FUNCTION lookup; mysql> DROP FUNCTION reverse_lookup;
CREATE FUNCTION と DROP FUNCTION ステートメントは
mysql データベース内のシステムテーブル func を更新します。
関数の名前、型、共有ライブラリ名はテーブルに保存されます。作成と破棄機能
のためには、mysql データベースに insert と
delete 特権を持つ必要があります。
既に生成してある関数を追加するために CREATE FUNCTION を使用すべき
ではありません。関数の再インストールが必要な場合は、DROP FUNCTION
でそれを削除し、それから CREATE FUNCTION で再インストールすべきで
す。例えば、関数の新しいバージョンの再コンパイルをした場合、
mysqld が新しいバージョンを得るために、これを行なう必要があるでしょ
う。そうでなければ、サーバは古いバージョンの使用を継続します。
mysqld を --skip-grant-tables オプションで起動しなくても、
有効な関数はサーバの開始毎に再読み込みされます。この場合、UDF 初期化は飛
ばされ、UDF が無効になります。(有効な関数は CREATE FUNCTION でロー
ドされているもので、DROP FUNCTION で削除されていないものです。)
ネイティブ関数を追加するためのプロシジャを以下に示します。バイナリ配布に はネイティブ関数を追加できないことに注意してください。プロシジャは MySQL ソースコードの変更を必要とするためです。MySQL を ソース配布から自分でコンパイルする必要があります。また、MySQL の他のバージョンに移行する場合(例えば、新しいバージョンがリリースされた 時)、新しいバージョンでこのプロシジャを繰り返す必要があります。
新しいネイティブ MySQL 関数を追加するためには、次のステップに従っ てください:
sql_functions[] 配列内で関数名を定義している `lex.h' に1行
追加してください。
yacc が定義す
べきプリプロセッサシンボルを指示します(これはファイルの先頭に追加すべき
です)。それから関数パラメータを定義し、これらのパラメータとともに
``item'' を simple_expr パース規則に追加します。例えば、これがど
のように動作するかを見るには、`sql_yacc.yy' 内の SOUNDEX の
全ての出来事をチェックしてください。
Item_num_func または Item_str_func から継承するクラス
を宣言してください。
double Item_func_newname::val() longlong Item_func_newname::val_int() String *Item_func_newname::Str(String *str)
void Item_func_newname::fix_length_and_dec()この関数は与えられた引数に基づいて少なくとも
max_length を計算す
べきです。max_length は関数が返し得る文字の最大数です。この関数は、
メイン関数が NULL 値を返すことがない場合は、maybe_null = 0
も設定すべきです。関数は、引数の maybe_null 変数をチェックするこ
とで、関数の任意の引数が NULL を返しうるかどうかをチェックできま
す。
全ての関数はスレッド安全である必要があります。
文字列関数については、次のいくつかの追加検討事項があります:
String *str 引数は、結果を保持するために使用される文字列バッファ
を提供します。
Go to the first, previous, next, last section, table of contents.