UDF のメカニズムが機能するためには、関数を C または C++ で記述し、オペレーティングシステムが動的ロードをサポートしている必要があります。MySQL ソース配布には、5 つの UDF 関数を定義している sql/udf_example.cc
ファイルが含まれています。UDF の呼び出し規則のしくみを理解するには、このファイルを参照してください。include/mysql_com.h
ヘッダーファイルには UDF 関連のシンボルおよびデータ構造体が定義されていますが、このヘッダーファイルを直接インクルードする必要はなく、mysql.h
によってインクルードされます。
UDF に含まれているコードは実行中のサーバーの一部になるため、UDF を記述するときは、サーバーコードを記述するときに適用されるすべての制約に従う必要があります。たとえば、libstdc++
ライブラリの関数を使用しようとすると、問題が生じることがあります。これらの制約は将来のサーバーのバージョンで変更されることがあるため、サーバーをアップグレードするときに、古いサーバー用にもともと作成された UDF を改訂する必要がある場合があります。これらの制約については、セクション2.9.4「MySQL ソース構成オプション」およびセクション2.9.5「MySQL のコンパイルに関する問題」を参照してください。
UDF を使用できるようにするには、mysqld を動的にリンクする必要があります。mysqld からシンボルにアクセスする必要がある UDF を使用する場合 (たとえば、sql/udf_example.cc
の metaphone
関数は default_charset_info
を使用します) は、-rdynamic
を指定してプログラムをリンクする必要があります (man dlopen
を参照してください)。
SQL ステートメントで使用する各関数について、対応する C (または C++) 関数を定義します。以降の説明では、「xxx」という名前を関数名の例として使用します。SQL と C/C++ での使用を区別するために、XXX()
(大文字) は SQL 関数の呼び出しを示し、xxx()
(小文字) は C/C++ 関数の呼び出しを示します。
C++ を使用する場合は、C 関数を次のようにカプセル化できます。
extern "C" { ... }
これにより、完成した UDF 内で C++ 関数名が読み取ることができる状態に維持されます。
次のリストは、XXX()
という名前の関数のインタフェースを実装するために記述する C/C++ 関数について説明しています。メインの関数 xxx()
は必須です。また、セクション24.3.2.6「ユーザー定義関数のセキュリティー上の予防措置」で説明している理由により、UDF にはここで説明している少なくとも 1 つのほかの関数が必要となります。
-
xxx()
メイン関数。ここで関数の結果が計算されます。SQL 関数のデータ型と C/C++ 関数の戻り型との対応を次に示します。
SQL の型 C/C++ の型 STRING
char *
INTEGER
long long
REAL
double
DECIMAL
関数を宣言することも可能ですが、現時点では値は文字列として返されるため、STRING
関数の場合と同様に UDF を作成します。ROW
関数は実装されていません。 -
xxx_init()
xxx()
の初期化関数。存在する場合は次の目的で使用できます。XXX()
への引数の数をチェックする。引数が目的の型であることを確認するか、メインの関数が呼び出されたときに引数を目的の型に強制的に変更するように MySQL に指示する。
メインの関数が必要とするメモリーを割り当てる。
結果の最大長を指定する。
結果の小数点以下の最大の桁数を指定する (
REAL
関数の場合)。結果として
NULL
を許容するかどうかを指定する。
-
xxx_deinit()
xxx()
の初期化解除関数。存在する場合、初期化関数によって割り当てられたすべてのメモリーを割り当て解除します。
SQL ステートメントによって XXX()
が呼び出されると、MySQL は初期化関数 xxx_init()
を呼び出して、引数のチェック、メモリーの割り当てなどの必要なセットアップを実行させます。xxx_init()
がエラーを返した場合、MySQL はエラーメッセージを出力して SQL ステートメントを中止し、メイン関数または初期化解除関数を呼び出しません。それ以外の場合、MySQL はメイン関数 xxx()
を行ごとに 1 回ずつ呼び出します。すべての行が処理されると、MySQL は初期化解除関数 xxx_deinit()
を呼び出し、必要なクリーンアップ処理が実行されます。
SUM()
のように動作する集約関数の場合は、次の関数も作成する必要があります。
-
xxx_clear()
現在の集約値をリセットしますが、新しいグループに対する初期集計値として引数を挿入しません。
-
xxx_add()
現在の集計値に引数を追加します。
MySQL は集計 UDF を次のように処理します。
xxx_init()
を呼び出して、集約関数が結果を格納するために必要なメモリーを割り当てます。GROUP BY
式に従ってテーブルをソートします。新しいグループになるたびに先頭行で
xxx_clear()
を呼び出します。同じグループに属する各行に対して
xxx_add()
を呼び出します。グループが変更されたとき、または最後の行が処理されたあとに、
xxx()
を呼び出して集約の結果を取得します。すべての行が処理されるまでステップ 3 から 5 までを繰り返します。
xxx_deinit()
を呼び出して、UDF が割り当てたメモリーを解放します。
すべての関数はスレッドセーフである必要があります。これにはメイン関数だけでなく、初期化関数および初期化解除関数のほか、集約関数によって必要とされる追加の関数も含まれます。この要件により、変化するグローバル変数または静的変数を割り当てることができなくなります。メモリーが必要な場合は、メモリーを xxx_init()
で割り当て、xxx_deinit()
で解放するようにしてください。