MySQLのユーザー定義関数(UDF)は大昔に作った記憶があるけど、最近作ってなかったので試しに作ってみたメモ。
関数の中身はなんでも良かったんだけど、フィボナッチ数を求める fib()
を作ってみた。
ちゃんと知りたい場合は、マニュアルとかサンプルプログラムを見ましょう。
% gcc -shared -I /usr/local/mysql/include fib.c -o fib.so % sudo cp fib.so /usr/local/mysql/lib/plugin/ % /usr/local/mysql/bin/mysql -uroot mysql> create function fib returns integer soname 'fib.so'; Query OK, 0 rows affected (0.02 sec) mysql> select n, fib(n) from (values row(1),row(2),row(3),row(4),row(5),row(6),row(7),row(8),row(9),row(10)) as n(n); +----+--------+ | n | fib(n) | +----+--------+ | 1 | 1 | | 2 | 1 | | 3 | 2 | | 4 | 3 | | 5 | 5 | | 6 | 8 | | 7 | 13 | | 8 | 21 | | 9 | 34 | | 10 | 55 | +----+--------+ 10 rows in set (0.01 sec)
// UDFの詳細はマニュアルとサンプルプログラムを参照 // https://dev.mysql.com/doc/refman/8.0/en/adding-functions.html // https://github.com/mysql/mysql-server/blob/8.0/sql/udf_example.cc // // コンパイル方法 // gcc -shared -I /usr/local/mysql/include fib.c -o fib.so // 作成された so ファイルを plugin-dir ディレクトリに置く // 使用時: // create function fib returns integer soname 'fib.so'; // select fib(10); // 関数が不要になった場合は破棄できる: // drop function fib; #include <string.h> #include "mysql.h" #include "mysql/udf_registration_types.h" // 初期化 // 関数を使用するクエリの実行前に呼ばれる // initid : 戻り値 // args : 引数 // message : エラーメッセージ用バッファ。MYSQL_ERRMSG_SIZE バイト(8.0 では 512) // 戻り値は、成功時には false、エラー時には true (変なの) // 関数の戻り値は CREATE FUNCTION で指定される bool fib_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { initid->maybe_null = true; // 戻り値がNULLになり得る場合はtrue initid->decimals = 0; // 戻り値が浮動小数点数の場合に小数点以下の桁数を指定 initid->max_length = 21; // 戻り値の最大文字数/最大桁数 initid->ptr = NULL; // この関数用に確保したメモリのポインタ等 if (args->arg_count != 1) { // 引数が1個じゃなければエラー strcpy(message, "fib() requires one argument"); return true; } if (args->maybe_null[0]) { // 引数にNULLは許容しない strcpy(message, "fib() requires not NULL"); return true; } if (args->arg_type[0] != INT_RESULT) { // 引数が整数でなければエラー strcpy(message, "fib() requires an integer argument"); return true; } // 上のようにチェックする他に MySQL に自動変換させることもできる //args->arg_type[0] = INT_RESULT; // おまけ // args->attribues[0] と args->attribute_lengths[0] で 0番目の引数に与えた名前がわかる // fib(123) の場合は "123", fib(hoge) の場合は "hoge" return false; } // 終了 // initid->ptr にメモリを確保していた場合はここで解放する void fib_deinit(UDF_INIT *initid) { } // 実行 // initid : ptr を使う場合以外は使用しない // args : 引数 // is_null : 戻り値がNULLになる場合 true を設定 // error : エラー時に true を設定。ひとつのクエリ内ではそれ以降は評価されずにNULLが返る long long fib(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) { long long n; n = *((long long *)args->args[0]); long long i, a, b, c; for (i = 0, b = 1, c = 0; i < n; i++) { a = b; b = c; c = a + b; if (c <= 0) { // オーバーフロー時は NULL を返す *is_null = true; return 0; } } return c; }