ようこそゲストさん

CPA-LABテクニカル

2008/02/02(土) Cakephpでsqliteの続き。editとdeleteの修正

はてブ情報 はてブに登録 はてブ数 cakephpspok
(追記)もっと効率的な修正方法があります。CakePHP1.2bとSQLite不具合のもっと簡単な直し方


以前にSQliteでのnew時(insert)の不具合を修正したのだけれど、今度は、editとdeleteでも不具合があることがわかった。しばらくMySQLのお勉強をしていたので、sqliteはほっておいたんだけど、やっぱりsqliteが恋しいので戻ってきた。


こちらでも、同じ認識の方がいるらしい。
CakePHPではまったこと15(sqliteを使っているときのupdate)
この方、いろいろtips書いておられて、頭が下がります。


なお、これらの不具合は、次期バージョンでは修正されるらしいので、つかのまのtips。これを書いている今でも、本家がアップデートしたら、この記事の意味はありません。また scaffoldもどきでの動作は確認していますが、自分でsave文は書いていないのでそこは未テストです。


そして、このeditとdeleteは、cakephpが作るSQL文の最終形をハックする形にしたので、複雑なテーブル構成の場合は、何らかの不具合がでる可能性が大いにあります。なお、テーブル名にピリオドがあるとそれだけでNGです。

環境
cake_1.2.0.6311-beta
SQLite2  ←3ではありません。
まずは、どこがおかしいのか列挙してみよう。以下は、MySQLでは通っても、SQLiteではNGのものである。
まずは、DELETEのSQL文から
  1. DELETE TabelName FROM TableName
    というあり得ない形になっていた。真ん中のテーブル名が不要
  2. WHERE FieldName IN('1')
    みたいな形になっていた。sqliteでは、deleteでIN句は使用不能。WHEWRE FieldName='1'であることが必要。
  3. "TableName"."FieldName" as "TabaleAlias"."FieldName"
    になっていた。deleteでasは使えないので、as以降を消去。deleteで複数テーブルを一気に消去することはないだろうから、たぶん大丈夫。
で、EDIT(UPDATE文)についても、上記1以外はすべてあてはまる。
本来であれば、SQL文の生成過程を修正するのが王道であり、おそらく本家はそのように対処するのでしょうが、それはとてつもなく大変なので、こんな感じの「応急措置」です。


では、いってみましょうか。


まずは、
  • \cake\libs\model\datasources\dbo\dbo_sqlite.phpを
  • \app\models\datasources\dbo\dbo_sqlite.phpにこぴーして、以下の修正&追加。
(フォルダがない場合は作りましょう。)
function _execute($sql) {
    return sqlite_query($this->connection, $sql);
}
を探しだし、以下のように修正する。
function _execute($sql) {
    return sqlite_query($this->connection, $this->spok_escape($sql));
}
そう、SQLの実行直前でエスケープするという、脆弱性を生みかねない危険な方法。


そして、このファイルの一番下の「}」直前に、以下を付け加える。
なお、わかる人からみたら、「無謀な修正」なので、自己責任で。私の少ないテストしか経ていないので、他で動作しない可能性もたくさんあります。
function spok_escape($sql){
    #UPDATE修正
    if( substr($sql,0,6)==='UPDATE' ) {
        #テーブルasの除去
        $sql=preg_replace("/\sAS\s\"[^\"]+\"/",' ',$sql);
        #テーブル名の除去
        $sql=preg_replace("/\s\"[^\"]+\"\./",' ',$sql);
        #INを=に
        $sql=preg_replace("/\sIN\s*\('/"," ='",$sql);
        #INの後ろカッコ除去
        $sql=preg_replace("/'\)/","' ",$sql);
    }

    #DELLTE修正
    if( substr($sql,0,6)==='DELETE' ) {
        #deleteとfromの間にテーブルが入っていたのを除去
        $sql=preg_replace("/[^\.]*FROM/",'DELETE FROM',$sql);
        #inの処理
        $sql=preg_replace("/\sIN *\('/"," ='",$sql);
        $sql=preg_replace("/'\)/","' ",$sql);
        #テーブルasの除去
        $sql=preg_replace("/\sAS\s\"[^\"]+\"/",' ',$sql);
        #テーブル名の除去
        $sql=preg_replace("/\s\"[^\"]+\"\./",'  ',$sql);
    }

    return $sql;

    }

正規表現なので、//の間を一字一句間違えても動作しません。なお、バックスラッシュは半角の¥マークです。このブログでは、システムの関係で、/になっております。コピペで大丈夫と思うけど。
本当は preg_replaceをつなげなくて(みっともない)、配列に一気に渡したほうがいいのだと思うけど、試行錯誤でこうなったので、このままにしておきます。
IN句のカッコをとるところなんかはいいかげんで、正直怖いです。何か別にカッコを使っていれば、それを取ってしまうので、そこで新たな不具合を生むおそれアリ。(このスクリプト全般がそうなんだけど)


(追記)
入力データの中に ') や IN('  や "." などがあると消去されます。入れるデータによってはうまくいかないです。(このようなやり方では仕方ない)


繰り返しですが時期バージョンでは修正されるそうなので、そうなったらすぐに本家に従いましょう。
SQLiteファンとしては早く対処して欲しい。しかし無料だから強くもいえんしね。皆さんもドネーションぐらいはしましょう。paypalで20秒でできちゃったよ。


なお、この同じファイルで、SQliteでのnew時(insert)の不具合を修正をやっておかないとinsertできません。

うーむ。ベータ版とはいえ。。。楽じゃないよcakephp。

1:   2008年07月23日(水) 深夜1時24分

 


名前:  非公開コメント   

  • TB-URL  http://cpa-lab.com/tech/045/tb/
  • CakePHP1.2bとSQLite不具合のもっと簡単な直し方 CPA-LABテクニカル spok
    私、nightlyって知りませんでした。ソフト開発の世界では、nightly buildとは、開発途上版ということなのですね。で、CakePHPでは、そのnightly buildが公開されていると。だからそれを使えばいいだぞ、と。こちらCakePHPでは...