2013-03-04
DBD::mysql の不具合っぽい挙動を見つけたので、とりあえず issue あげてみた
きっかけはこの記事。DBIのプレースホルダーで起こった謎な挙動 - $shibayu36->blog;
「うーん、どう見てもバグっぽいな」と思い、また、自分がメンテしてるDBD::mysqlPPで悩みつつ LIMIT のプレースホルダーを実装したことがあったので、「参考になるかなー」と思って、本家の方のソース読んでみた。
本家だから、かっちょいいパーサとか組んで SQL ステートメント読んでるものとばかり思っていたのだけど、根性でパースしてる感じのソースで、「これは大変だなー」って思った。で、問題っぽいのがこれ。 dbdimp.c 602行目あたり。
良く見れば分かりますが、「limit ?」or「LIMIT ?」にしかマッチしません。ですので、元記事にあった「limit ?」(スペース2つ)とか、「LIMIT 1 OFFSET ?」とかを bind しようとすると、 limit_flag が立たずに意図せずクォートされてしまって、シンタックスエラーになってしまうみたいです。
回避は容易なので、ほっといても良いのかもですが、怪しい英語頑張って書いて、 github issue にあげときました。(どこにレポートあげるのが正しいのか良く分からなかったのだけど、github で良かったのだろーか?…怖い文面の自動リプライとか来なかったので、一応大丈夫だとは思いますが。。。)
直すなら、「upper case か lower case どちらかに統一したうえで、”limit” にマッチさせ、次の token が “?” だったら limit_flagを立てる」、ってのが正しい修正なのでしょうけど、超絶めんどくさいな。C言語での文字列処理だし。。。自分だったらやらないw。
こういうちょっと変なパターンとか、サブクエリ中の LIMIT とか、コメントアウトとか、この辺の実装を真面目にやろうとすると頭痛くなるようなパターン多数ありますからねー。
server side prepare 改善してくとか、mysql のクライアントライブラリが公式のパーサを用意してくれるとかなると、もうちょっとなんとかなりそうな気もしますが、現状は「LIMIT, OFFSET に bind したい場合、LIMIT の後ろは空白1つ。LIMIT と OFFSET の両方にプレースホルダ置いとく」ってのが無難かな。完全に Workaround だけど。
動的プレースホルダ実装しているドライバ(大抵のデフォルトはそうだと思うんだけど)は同じ悩みかかえてるはずなんですけど、他の言語だとどうなってるんですかねー?