クソクエリーについての名言 がつい先週生まれたばかりですが、みなさま如何お過ごしでしょうか。そういえば今年は Kuso-query As A Code みたいな話もさせてもらいました。
過去、現在、未来、全宇宙に存在する全てのクソクエリーを、生まれる前に自分の手でカジュアルに消し去るため(仮)に、MySQL 5.7.5-labsのQuery Rewrite Plugin の記事では触れるだけだったオレオレrewriteプラグインを書いてみました。君のクエリにレボ☆リューション! (仮)
どういう動作をさせるかというと、
mysql57> SELECT 1; +---+ | 1 | +---+ | 1 | +---+ 1 row in set (0.00 sec) mysql57> SELECT (1); +---+ | 1 | +---+ | 1 | +---+ 1 row in set (0.00 sec) mysql57> SELECT ((1)); +---+ | 1 | +---+ | 1 | +---+ 1 row in set (0.00 sec) mysql57> SELECT (((1))); ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'Your query is f**king!!' at line 1 mysql57> SELECT ((1)), ((2)); +---+---+ | 1 | 2 | +---+---+ | 1 | 2 | +---+---+ 1 row in set (0.00 sec) mysql57> SELECT (((1)), ((2))); ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'Your query is f**king!!' at line 1 mysql57> SHOW WARNINGS; +-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Note | 1105 | Query 'SELECT (((1)), ((2)))' rewritten to 'Your query is f**king!!' by plugin: rewrite_test. | | Error | 1064 | You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'Your query is f**king!!' at line 1 | +-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 2 rows in set (0.00 sec)
シンプルな話、クソっぽいクエリー(今実装してあるのは、かっこが3つ以上ネストしているもの)が来た時に強制的にクエリーを上書きします。そしてこの文字列はSQLとして成立していないので、 *必ず* シンタックスエラーになります。
パーサーのレイヤーで書き換えているので、スローログとかもこの通り。
$ tail slow.log # User@Host: root[root] @ localhost [] Id: 2 # Query_time: 0.000212 Lock_time: 0.000000 Rows_sent: 0 Rows_examined: 0 use d1; SET timestamp=1417408435; SET SESSION long_query_time= 0; # Time: 2014-12-01T04:33:56.545809Z # User@Host: root[root] @ localhost [] Id: 2 # Query_time: 0.000088 Lock_time: 0.000000 Rows_sent: 0 Rows_examined: 0 SET timestamp=1417408436; Your query is f**king!!;
これでもう2度と、派生テーブル同士を相関サブクエリーで結合したものをUNION DISTINCTで結合するとかいうクソクエリーに二度とお目にかかることはありません。もうちょっと真面目に書けば、テーブルを4つ以上JOINしてるとか、CREATE TABLEの中のflgxなんてカラム名に反応させてリライトさせることも可能です。
さて、ではこんなオレオレぷらぎんの作り方を解説します。こっちが本題だよ。
ビルドはt2.smallにamzn-ami-hvm-2014.09.1.x86_64-ebs (ami-b66ed3de)のAMIでやっています。t2.microだとメモリー食いきられてmakeできなかった。。URLは2014/12/01現在のものです。
まず、何はなくともlabs.mysql.comから"MySQL Optimizer/InnoDB/Replication" のソースコードを落としてこないとなんだけど、なんかどうも2ヶ月くらい前からファイルの終盤で"Connection reset by peer"を食らうようになっちゃってます。なんだろうこれ。wgetががんばってリトライしてくれるので、それほど困ってませんが。
$ wget http://downloads.mysql.com/snapshots/pb/mysql-5.7.5-labs-preview/mysql-5.7.5-labs-preview.tar.gz $ tar xzf mysql-5.7.5-labs-preview.tar.gz $ cd mysql-5.7.5-labs-preview $ sudo yum install -y cmake gcc gcc-c++ ncurses-devel
Write Yourself a Query Rewrite Plugin: Part 1 | MySQL Server Blog から読み取るに、プラグインそのもののソースと"plug.in"ファイル、CMakeLists.txtを放り込んでやればいいらしい。
$ cd plugin/ $ ll rewrite_example total 12 -rw-r--r-- 1 ec2-user ec2-user 835 Sep 24 05:06 CMakeLists.txt -rw-r--r-- 1 ec2-user ec2-user 278 Sep 24 05:06 plug.in -rw-r--r-- 1 ec2-user ec2-user 2697 Sep 24 05:06 rewrite_example.cc $ cp -r rewrite_example mysql_casual $ cd mysql_casual/
というわけで公式のrewrite_exampleをスケルトンにして実装していく。まずはCMakeLists.txtでこれは難しくない。i_sぷらぎんとかと同じで元になるソースの名前とモジュールの名前を決めてやるだけ。
$ vim CMakeLists.txt .. MYSQL_ADD_PLUGIN(do_not_allow_kuso_query mysql_casual.cc MODULE_ONLY MODULE_OUTPUT_NAME "mysql_casual")
"plug.in"も同じ感じだった。ダイナミックリンクするときのファイル名と、スタティックリンクする時のファイル名を指定する? (i_sぷらぎんの時には書かなかったなこれ)
$ vim plug.in MYSQL_PLUGIN(do_not_allow_kuso_query, [Be extinct all of kuso-query, before those are born.]), [Example query rewrite plugin by yoku0825.] MYSQL_PLUGIN_DYNAMIC(do_not_allow_kuso_query, [mysql_casual.la]) MYSQL_PLUGIN_STATIC(do_not_allow_kuso_query, [mysql_casual.a])
で、核になるリライトぷらぎん本体。Query rewrite用にAPIが切ってあるので、それを呼ぶ感じで実装していく。やっぱりrewrite_example.ccをリネームしてそこを直してみるのが早い。
$ mv -i rewrite_example.cc mysql_casual.cc $ vim mysql_casual.cc .. static st_mysql_rewrite_pre_parse rewrite_example_descriptor= { MYSQL_REWRITE_PRE_PARSE_INTERFACE_VERSION, /* interface version */ your_query_is_fxxking_dude, /* rewrite raw query function */ free_rewritten_query, /* free allocated query */ };..
先頭からQuery Rewrite用APIのバージョン, クエリーのリライトに使う実際の関数, 実行後にfreeするための関数。
先頭と最後はいじらなくていいので、真ん中だけそれっぽい名前にいじる(もちろん名前は変えなくてもいい)
$ vim mysql_casual.cc .. mysql_declare_plugin(rewrite_example) { MYSQL_REWRITE_PRE_PARSE_PLUGIN, &rewrite_example_descriptor, "do_not_allow_kuso_kuery", "yoku0825", "Making your f**king query to be syntax error :)", PLUGIN_LICENSE_GPL, rewrite_plugin_init, NULL, 0x0001, /* version 0.0.1 */ NULL, /* status variables */ NULL, /* system variables */ NULL, /* config options */ 0, /* flqgs */ } mysql_declare_plugin_end;
i_sぷらぎんとか書いているとおなじみの、mysql_declare_plugin構造体。説明文とかAUTHORをちょろっといじる。
$ vim mysql_casual.cc .. static int your_query_is_fxxking_dude(Mysql_rewrite_pre_parse_param *param) { unsigned depth= 0, max_depth= 0; for (unsigned i= 0; i < param->query_length; i++) { if (param->query[i] == '(') { depth++; if (depth > max_depth) max_depth= depth; } else if (param->query[i] == ')') depth--; } if (max_depth > 2) { param->rewritten_query= strdup("Your query is f**king!!"); param->rewritten_query_length= strlen("Your query is f**king!!"); param->flags|= FLAG_REWRITE_PLUGIN_QUERY_REWRITTEN; } else { param->rewritten_query= new char[param->query_length + 1]; param->rewritten_query_length= param->query_length; strncpy(param->rewritten_query, param->query, param->query_length + 1); } return 0; } ..
オリジナルではrewrite_lower関数になっているやつ(書き換えの本体)をいじくります。Mysql_rewrite_pre_parse_paramの構造体については include/mysql/plugin_query_rewrite.h に定義があるので、そこを見ればなんとなくわかるかと。param->queryにもともとのクエリーが入っていて、param->rewritten_queryに変換後のクエリーが入る感じですね。カッコのネストの深さを数えて、一定以上だったら書き換え、そうでなければそのまま通すような関数にしました。
$ cmake -DDOWNLOAD_BOOST=1 -DWITH_BOOST=/tmp/my_boost $ make $ sudo make install
長いのでお茶でも淹れてきましょうかね。。
$ cd /usr/local/mysql $ sudo useradd mysql $ sudo ./bin/mysql_install_db --user=mysql --datadir=./data --basedir=./ --insecure $ sudo chown -R mysql. /usr/local/mysql/data $ sudo ./bin/mysqld_safe & $ bin/mysql -uroot
5.7.5からmysql_install_dbはbinの下に移動になったんですよね。あと、basedirを与えないとエラーになって怒る。--insecureはデフォルトのランダムパスワードを設定しないという待望のオプションです。
mysql> INSTALL PLUGIN do_not_allow_kuso_query SONAME 'mysql_casual.so'; Query OK, 0 rows affected (0.00 sec) mysql> SELECT 1; +---+ | 1 | +---+ | 1 | +---+ 1 row in set (0.00 sec) mysql> SELECT (1); +---+ | 1 | +---+ | 1 | +---+ 1 row in set (0.00 sec) mysql> SELECT ((1)); +---+ | 1 | +---+ | 1 | +---+ 1 row in set (0.00 sec) mysql> SELECT (((1))); ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'Your query is f**king!!' at line 1 mysql> SHOW WARNINGS; +-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Level | Code | Message | +-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ | Note | 1105 | Query 'SELECT (((1)))' rewritten to 'Your query is f**king!!' by plugin: do_not_allow_kuso_query. | | Error | 1064 | You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'Your query is f**king!!' at line 1 | +-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ 2 rows in set (0.00 sec)
はい、出来上がりです! とてもカジュアルでしたね。明日からきっと雨後のたけのこのようににょきにょきとクエリーリライトぷらぎんが現れることでしょう。
寒い日が続くようですが、みなさまお風邪など召しませんように。
明日は @karupanerura さんです!
0 件のコメント :
コメントを投稿