Bug(バグ) #3135
未完了サブクエリのWHERE節に「field IN ?」の形式のDQLがあると Invalid parameter number エラーが発生する
0%
説明
Overview (現象)¶
Doctrine でサブクエリの WHERE 節に filed IN ?
の形式のものがあると、 DQL から SQL を生成する際に Invalid parameter number のエラーが発生する。
SELECT 〜 WHERE field = (SELECT field FROM table WHERE field IN ?);
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens
次のようにパラメーター数分の ?
が DQL 時点で展開されている場合はエラーは発生しない。
SELECT 〜 WHERE field = (SELECT field FROM table WHERE field IN (?, ?, ?));
具体的には以下のようなコードでエラーが発生する。
例1 : サブクエリの生成に andWhereIn() を利用する場合
opActivateBehavior::disable(); // あってもなくてもエラー $q = Doctrine_Core::getTable('MemberConfig')->createQuery('c'); $q2 = $q->createSubquery() ->select('m.id') ->from('Member m') ->andWhereIn('m.id', array('1', '2')); $q->andWhere('c.member_id IN ('.$q2->getDql().')'); $q->execute();
例2 : サブクエリを手動で記述する場合
opActivateBehavior::disable(); // あってもなくてもエラー $q = Doctrine_Core::getTable('MemberConfig')->createQuery('c'); $q->andWhere('c.member_id IN (SELECT m.id FROM Member m WHERE m.id IN ?)', array(array('1', '2'))); $q->execute();
※ただし、上の2つの例は APC のキャッシュが有効な状態では2回目以降のアクセスで成功していた
本問題は基本的には Doctrine のバグである可能性が高いが、例1 に関しては opDoctrineQuery を利用しない場合はエラーが発生しないため、OpenPNE の問題とも考えられる可能性がある。
本問題は #3052 の調査中に発見した。(#3052 は 例1 のケースに該当する)
Causes (原因)¶
DQL から SQL を生成するタイミングで filed IN ?
の形式は、 field IN (?, ?, ?)
のようにパラメーター配列の要素数に応じた形式に変換されるが、サブクエリ内の変換処理を行なう際にパラメーターを正しく扱えていないようで、要素数を間違ったり、パラメーターが展開されなかったりでエラーになる。
opDoctrineQuery::andWhereIn() は、パフォーマンスチューニングのため filed IN ?
の形式の DQL を生成するが (#991)、親クラスの Doctrine_Query_Abstract::andWhereIn() をそのまま使う場合は DQL 時点でパラメーターの展開が行われるためこの問題が発生しない。
Way to fix (修正内容)¶
例1 だけを修正するのであれば、以下のようにサブクエリ利用前提の場合はパフォーマンスチューニングのためのコードを利用しないという修正案が考えられます。
--- a/lib/util/opDoctrineQuery.class.php +++ b/lib/util/opDoctrineQuery.class.php @@ -189,6 +189,11 @@ class opDoctrineQuery extends Doctrine_Query } } + if ($this->isSubquery()) + { + return parent::andWhereIn($expr, $params, $not); + } + $this->addWhereInCount(count($params)); if ($not)
ただし、この修正では例2は動作しないため修正としては不十分かもしれない。