なにしろ今使っているSQLマッパー(ORMなのか?)は2002年ごろ作ったコードを基にしてます。改良を加えつつ、使ってたのですが、いよいよPDOとプリペアドステートメントへ対応させることに決定。
しかし・・・PDOも、そしてPrepared Statementも、
なんて使いづらいんだ!
◆Prepared Statementへの対応方法
方向としては、下記のように名前つきのパラメータ(以下、ホルダーと呼称)を使って構築します。
// 古きよきqueryを使った方法 $rdb->query( "SELECT * FROM table WHERE id='1234'" ); // Prepared Statementを使った方法 $h = $rdb->prepare( "SELECT * FROM table WHERE id=:id" ); $h->execute( array( ':id' => '1234' ) );
どこかで、'1234'と':id'を結び付けておく必要があります。
次のような関数でホルダーを一元管理することにしました。
function getHolder( &$val ) { static $prepare_values=array(); if( $val ) { $holder = ':prep_' . count( $prepare_values ); $this->prepare_values[ $holder ] = $val; $val = $holder; } return $prepare_values; } $id = '1234'; getHolder( $id ); $h = $rdb->prepare( "SELECT * FROM table WHERE id={$id}" ); $h->execute( getHolder() );
SQLマッパーはクラスなので実際はメソードですが、こいつをマッパーのあちこちに仕掛けておけば、簡単に対応可能、というわけです。
◆PDOではrowCountが使えない
薄々知ってましたが、SELECTを発行した場合、結果セットの総数が分かりません。「SELECT COUNT(*) ...」で対応しろという話です。
仕方がないので、対応しました。
「SELECT ~ FROM...」の「~」部分をCOUNT(*)に無理やり変更。
SELECT文を保存しておいて、rowCountが呼ばれたら、裏でSQL文を変更して、発行して、数を数えて、返す。
お、動いた、動いた。
が、これとPrepared Statementを組み合わせると大変な事態に。
◆MySQLのHY093エラー
MySQLで動作テスト開始をしたとたんのエラーコード。
テストのXAMPP環境だとエラーメッセージすら出ない。
調べると、「無効なパラメータ番号」という意味らしい。
なんとexecuteで与える配列で使ってないデータがあるとエラーになるらしい。
どんだけ細かいんだよ!
と文句をいいながら、適度なタイミングで「$prepare_values」をクリアすることに。この適度なタイミングってのが難しい。使っているうちにぐちゃぐちゃになりそうだ。
◆で、気がついた。
Prepared Statementを使って数える場合、万が一COUNT(*)に変更した部分にホルダーがあったら、絶対に動かんじゃないか。たとえば
例) SELECT :what AS what, id, name FROM table WHERE type=:type SELECT COUNT(*) FROM table WHERE type=:type
これだとHY093エラーで動かないのは確実。
こういうSELECT文の場合、裏で上手カウントするのは無理っぽい。
使う人が、明示的にSQLを二つ作って対応するしかないか。
◆まだまだあるぞPDOの妙なエラー
こんなエラーメッセージもありました。
Cannot execute queries while other unbuffered queries are active. Consider using PDOStatement::fetchAll(). Alternatively, if your code is only ever going to run against mysql, you may enable query buffering by setting the PDO::MYSQL_ATTR_USE_BUFFERED_QUERY attribute.
書いてある通りにアトリビュート設定しても駄目。
バージョンの問題なのか?
原因は・・・
一度に二つのSQL文を渡してたからでした。
DROP TABLE IF EXIST table;
CREATE TABLE table ( ... );
ひとつずつ発行するとエラー無しになりました。
◆感想
新しいライブラリが登場しても、すぐ飛びつかない理由を思い出した。
マニュアルに出てこないような癖があるので避けてしまう。
面白いと思う人もいるけど、私は面倒くさがりだから。
0 件のコメント:
コメントを投稿