

オレオレ言語全盛期の炎暑の候、いかがお過ごしでしょうか。

オレオレSQLの作り方〜PostgreSQL編〜に続き、オレオレSQLの作り方〜MySQL編〜という事で

MySQLに、自作のSQL関数(UDF)を組み込むやり方を簡単に紹介します。

MySQLは名の通り、私のSQLなのでオレオレSQLには持ってこいですね。

簡単に言えば

select oreore();

select oreore(column1, column2) from table1;

自分で定義したC言語のoreore関数をDBに組み込んで使おうという事です。

最終目標は、SQLの結果でJSONを返そうと思います。

select json(a, b) from c;

の結果で

{ "aaa" : 1, "bbb" : "abc" }

固定値を返す関数 早速、固定で999を返す関数を作ってみます。

※バージョンはMySQL5.0.62です。インストール等は省略します。 #include <stdio.h> #include <mysql/mysql.h> #ifdef __cplusplus extern "C" { #endif my_bool ore1_init(UDF_INIT *initid, UDF_ARGS *args, char *message); void ore1_deinit(UDF_INIT *initid); long long ore1(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error); #ifdef __cplusplus } #endif my_bool ore_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { return 0 ; } void ore_deinit(UDF_INIT *initid) { } long long ore(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) { return 999 ; } ※MySQL4xのマニュアルだと xxx_init や xxx_deinit は省略可能とありますが

MySQL5xだとxxx_initが無いと関数登録時にエラーになるようです これをコンパイルし、/usr/lib へコピーします。

※LD_LIBRARY_PATHに追加するやり方が望ましいですが、今回は/usr/libへコピーします



cc -shared -I /usr/include/mysql -o oreore.so oreore.c cp oreore.so /usr/lib 作成したオブジェクトの関数を呼び出す関数を定義します。

create functionを使用します。 mysql> create function ore returns integer soname 'oreore.so' ; Query OK, 0 rows affected ( 0 . 00 sec)

では、早速作った関数を実行 mysql> select ore(); +-------+ | ore() | +-------+ | 999 | +-------+ 1 row in set (0.00 sec) 999が正しく表示されていますね。 SQLの型とC/C++の型の対応は以下の通りです。 SQL の型 C/C++ の型 STRING char * INTEGER long long REAL double

オレオレSQL開発でのデバッグ方法 デバッグ作業の効率をあげるために、フロントエンドにメッセージを出力してみます。

my_dbug.hのDBUG_PRINTを使用したりしたのですが、PostgreSQLのelog関数のようなものは

見あたりませんでした。

xxx_init内では引数チェック等を行い、xxx_initの引数messageにメッセージを設定可能ですが

xxx関数ではmessageは使用出来ない為、stderrにメッセージを出力しログファイルで確認する

方法が容易かと思います。

先ほどのoreore.cに1行追加します fprintf( stderr , " %s

" , "デバッグのテストですよ" ); return 999 ; 再度、コンパイルしUDFを登録しなおすと/var/log/mysqld.logに デバッグのテストですよ が出力されています。

ログをtailしながら開発するのが楽です。





引数を取得 次に引数を取得してみます。

テーブル作成 mysql> create table table1 (column1 int, column2 varchar(32)); Query OK, 0 rows affected (0.00 sec) mysql> describe table1; +---------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------+-------------+------+-----+---------+-------+ | column1 | int(11) | YES | | NULL | | | column2 | varchar(32) | YES | | NULL | | +---------+-------------+------+-----+---------+-------+ 2 rows in set (0.00 sec) oreore.cを少し修正します。 引数チェック my_bool ore_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { if (args->arg_count != 2 ) { strcpy(message, "ore() requires two arguments!" ); return 1 ; } if (args->arg_type[ 0 ] != INT_RESULT || args->arg_type[ 1 ] != STRING_RESULT) { strcpy(message, "ore(1, 'a') requires a integer and an string!" ); return 1 ; } return 0 ; } 引数を取得 long long ore(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) { long long arg1 = *(( long long *)args->args[ 0 ]); const char *arg2 = args->args[ 1 ]; fprintf( stderr , "arg1=[ %lld ] arg2=[ %s ]

" , arg1, arg2); return 999 ; } ここで、先ほどの引数無しのoreore関数を削除しておきます。

登録した関数を削除するには、drop function ユーザ関数 を使います。



mysql> drop function ore; Query OK, 0 rows affected ( 0 . 00 sec) mysql> create function ore returns integer soname 'oreore.so' ; Query OK, 0 rows affected ( 0 . 00 sec) 早速、動かしてみます。

引数チェックを確認してみましょう。



引数無し mysql> select ore(); ERROR: ore() requires two arguments! 引数の数が不正 mysql> select ore(1,1,1); ERROR: ore() requires two arguments! 引数の型が不正 mysql> select ore('AAA', 111); ERROR: ore(1, 'a') requires a integer and an string!" 正常系 mysql> select ore(123, 'test'); +------------------+ | ore(123, 'test') | +------------------+ | 999 | +------------------+ 1 row in set (0.00 sec)

ログには arg1=[123] arg2=[test] と表示されています

テーブルにデータを入れて実行してみましょう。 mysql> insert into table1 values(1, 'aaa'); Query OK, 1 row affected (0.00 sec) mysql> insert into table1 values(2, 'bbb'); Query OK, 1 row affected (0.00 sec) mysql> select * from table1; +---------+---------+ | column1 | column2 | +---------+---------+ | 1 | aaa | | 2 | bbb | +---------+---------+ 2 rows in set (0.00 sec) mysql> select ore(column1, column2) from table1; +-----------------------+ | ore(column1, column2) | +-----------------------+ | 999 | | 999 | +-----------------------+ 2 rows in set (0.01 sec)

ログファイルには arg1=[1] arg2=[aaa] arg1=[2] arg2=[bbb] それぞれのレコードに対応する引数が取得できていますね。





