SQL::Abstract で LIKE の ESCAPE を指定する
正規表現でいう ^a_c.*
みたいなのをひっかけようと思って,
my ($sql, @binds) = SQL::Abstract->new()->select( # table 'foo', # fields [ '*' ], # where { id => { LIKE => 'a_c%' }, }, );
みたくやると,a_code
だけでなく abc
もマッチしてしまう罠。
PostgreSQL だと,デフォルトでエスケープ文字が \
になってるので,
# where { id => { LIKE => 'a\\_c%' }, },
のようにエスケープすればいい(MySQL だとどうなんだろ)。
でも,たとえば SQLite はデフォルトでエスケープ文字が設定されてない。だから LIKE
などのあとに ESCAPE
を指定するのが本筋。なんだけど,SQL::Abstract でふつーにやろうと思うとうまくいかない。
最初全部をリテラル SQL で指定するか,SQL::Abstract を改造(拡張)する((sub _where_hashpair_HASHREF()
に手を加えればいけるかな,というとこまで調べた。))しかないかなぁと思ったんだけど,調べたらちゃんと逃げ道があった。それがプレースホルダつきリテラル SQL(Literal SQL with placeholders and bind values (subqueries) - SQL::Abstract)。
# where { id => \"IS NULL", },
みたいなやつのこと。
プレースホルダつきバージョンは,今回の場合,以下のように指定する。
# where { id => { LIKE => \[ '? ESCAPE ?', 'a\\_c%', '\\' ] }, },
もちろん
id => \[ 'LIKE ? ESCAPE ?', 'a\\_c%', '\\' ],
こう書いてもいい。
ようするに,配列リファレンスの「さらにリファレンス」を渡すと,第二引数以下をバインド値として,プレースホルダつき SQL が発行されるわけです。