Go to the first, previous, next, last section, table of contents.


14 MySQL への新しい関数の追加

MySQLに新しい関数を追加するには2つの方法があります:

それぞれの方法には、有利な点と不利な点があります:

新しい関数を追加するためにどちらの方法を使用しても、ABS()SOUNDEX() のようなネイティブ関数と同じように使用することができま す。

14.1 新しいユーザ定義関数の追加

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++ 関数の 戻り値型の対応は次に示します:
SQL 型 C/C++ 型
STRING char *
INTEGER long long
REAL double
xxx_init() (オプション)
xxx() の初期化関数。これは次のように使用されます:
xxx_deinit() (オプション)
xxx() の終了関数(deinitialization function)。これは初期化関数によっ て割り当てられたメモリを解放すべきです。

SQL ステートメントが XXX() を呼び出すとき、MySQL は引数 チェックやメモリ割り当てのように、必要なセットアップを行なわせるために、 初期化関数 xxx_init() を呼び出します。xxx_init() がエラー を返す場合、SQL ステートメントはエラーメッセージと共に異常終了し、メイン 関数と終了関数は呼び出されません。そうでなければ、メイン関数 xxx() が各行毎に呼び出されます。全ての行が処理された後、終了関数 xxx_deinit() が、必要な掃除を行なうために、呼び出されます。

全ての関数はスレッド安全でなくてはいけません(メイン関数だけでなく、初期 化関数と終了関数も同様です)。これは変更されうるグローバル変数や静的変数 を割り当てることは許されないことを意味します! メモリが必要な場合は、 xxx_init() で割り当て、xxx_deinit() でそれを解放すべきです。

14.1.1 UDF 呼び出しシーケンス

メイン関数は下に示すように宣言されるべきです。戻り値型と引数は、 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_null1 に設定すべきです。デフォルト値は、引数のい ずれかが maybe_null として宣言された場合、1 です。
unsigned int decimals
小数部の桁数。デフォルト値はメイン関数に渡される引数の小数部の最大桁数で す。(例えば、関数に 1.34, 1.345, 1.3 が渡された場合、 デフォルトは 3 です。1.345 が 3 桁の小数部を持つためです。)
unsigned int max_length
文字列結果の最大長。デフォルト値は関数の戻り値型に依存して異なります。文 字列関数では、デフォルトは最も長い引数の長さです。整数関数では、デフォル トは 21 桁です。実数関数では、デフォルトは 13 に initid->decimals で示される小数部の桁数を加えたものです。(数値関数では、長さは符号と小数 点文字を含みます。)
char *ptr
それ自身の目的のために関数が使用できるポインタです。例えば、関数は割り当 てたメモリを関数間で伝達するために、initid->ptr を使用できます。 xxx_init() では、メモリを獲得し、このポインタに割り当てます:
initid->ptr = allocated_memory;
xxx()xxx_deinit() では、initid->ptr を参照して、 メモリを使用または解放します。

14.1.2 引数処理

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 要素を求める型に設定できます。これは MySQLxxx() の各呼び出しにこれらの型を引数に強制するようにさせます。例 えば、最初の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 を参照できます:
unsigned long *lengths
初期化関数では、lengths 配列は各引数についての最大文字列長を示し ます。メイン関数の各呼び出しについては、lengths は、現在処理され ている行に渡される任意の文字列引数の実際の長さを含んでいます。 INT_RESULT, REAL_RESULT 型の引数については、lengths はまだ引数の最大長を含んでいます(初期化関数については)。

14.1.3 戻り値とエラー処理

初期化関数は、エラーが無い場合は 0 を、そうでなければ 1 を 返すべきです。エラーが発生する場合は、xxx_init() は NULL 終端エラー メッセージを message パラメータに格納すべきです。このメッセージは クライアントに返されます。メッセージバッファは MYSQL_ERRMSG_SIZE 文字長ですが、80 文字より小さく保つように試みるべきです。標準的な端末画 面の幅にフィットするようにです。

メイン関数 xxx() の返す値は long longdouble 関 数については関数値です。文字列関数については、resultlength 引数で文字列が返されます。result は少なくとも 255 バイト長のバッファです。これらに内容と戻り値の長さを設定してください。例 えば:

memcpy(result, "result string", 13);
*length = 13;

文字列関数は通常は結果をポイントする値を返します。

メイン関数で NULL 値の戻り値を示すには、is_null1 に設定してください:

*is_null = 1;

メイン関数でエラーの戻り値を示すには、error パラメータを 1 に設定します:

*error = 1;

xxx() が任意の行について *error1 を設定する場合、 関数値は現在の行についてと、その後の XXX() が呼び出されるステート メントによって処理される任意の行について NULL です。(xxx() は続く行については呼び出しさえされません。) 注意: 3.22.10 より 前の MySQL バージョンでは、*error*is_null の 両方に設定すべきです:

*error = 1;
*is_null = 1;

14.1.4 ユーザ定義関数のコンパイルとインストール

UDF を実装するファイルはサーバが動作するホスト上でコンパイルされインストー ルされないければなりません。この処理はサンプル UDF ファイル `udf_example.cc' について次に説明します。これは MySQL ソー ス配布に含まれています。このファイルは次の関数を含みます:

動的ロード可能ファイルは、次のような何らかのコマンドを使用して、共有オブ ジェクトファイルとしてコンパイルされるべきです:

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 FUNCTIONDROP FUNCTION ステートメントは mysql データベース内のシステムテーブル func を更新します。 関数の名前、型、共有ライブラリ名はテーブルに保存されます。作成と破棄機能 のためには、mysql データベースに insertdelete 特権を持つ必要があります。

既に生成してある関数を追加するために CREATE FUNCTION を使用すべき ではありません。関数の再インストールが必要な場合は、DROP FUNCTION でそれを削除し、それから CREATE FUNCTION で再インストールすべきで す。例えば、関数の新しいバージョンの再コンパイルをした場合、 mysqld が新しいバージョンを得るために、これを行なう必要があるでしょ う。そうでなければ、サーバは古いバージョンの使用を継続します。

mysqld--skip-grant-tables オプションで起動しなくても、 有効な関数はサーバの開始毎に再読み込みされます。この場合、UDF 初期化は飛 ばされ、UDF が無効になります。(有効な関数は CREATE FUNCTION でロー ドされているもので、DROP FUNCTION で削除されていないものです。)

14.2 新しいネイティブ関数の追加

ネイティブ関数を追加するためのプロシジャを以下に示します。バイナリ配布に はネイティブ関数を追加できないことに注意してください。プロシジャは MySQL ソースコードの変更を必要とするためです。MySQL を ソース配布から自分でコンパイルする必要があります。また、MySQL の他のバージョンに移行する場合(例えば、新しいバージョンがリリースされた 時)、新しいバージョンでこのプロシジャを繰り返す必要があります。

新しいネイティブ MySQL 関数を追加するためには、次のステップに従っ てください:

  1. sql_functions[] 配列内で関数名を定義している `lex.h' に1行 追加してください。
  2. `sql_yacc.yy' に2行追加してください。一つは、yacc が定義す べきプリプロセッサシンボルを指示します(これはファイルの先頭に追加すべき です)。それから関数パラメータを定義し、これらのパラメータとともに ``item'' を simple_expr パース規則に追加します。例えば、これがど のように動作するかを見るには、`sql_yacc.yy' 内の SOUNDEX の 全ての出来事をチェックしてください。
  3. `item_func.h' 内で、関数が数値または文字列のどちらを返すかに依存し て、Item_num_func または Item_str_func から継承するクラス を宣言してください。
  4. `item_func.cc' 内で、数値または文字列のどちらの関数を定義するかに依 存して、次の宣言の一つを追加してください:
    double   Item_func_newname::val()
    longlong Item_func_newname::val_int()
    String  *Item_func_newname::Str(String *str)
    
  5. おそらく次の関数も定義すべきです:
    void Item_func_newname::fix_length_and_dec()
    
    この関数は与えられた引数に基づいて少なくとも max_length を計算す べきです。max_length は関数が返し得る文字の最大数です。この関数は、 メイン関数が NULL 値を返すことがない場合は、maybe_null = 0 も設定すべきです。関数は、引数の maybe_null 変数をチェックするこ とで、関数の任意の引数が NULL を返しうるかどうかをチェックできま す。

全ての関数はスレッド安全である必要があります。

文字列関数については、次のいくつかの追加検討事項があります:


Go to the first, previous, next, last section, table of contents.