プロジェクト

全般

プロフィール

Bug(バグ) #3135

未完了

サブクエリのWHERE節に「field IN ?」の形式のDQLがあると Invalid parameter number エラーが発生する

Rimpei Ogawa さんが12年以上前に追加. ほぼ5年前に更新.

ステータス:
New(新規)
優先度:
Normal(通常)
担当者:
-
対象バージョン:
開始日:
2012-07-27
期日:
進捗率:

0%

予定工数:
3.6 で発生するか:
Yes (はい)
3.8 で発生するか:
Unknown (未調査)

説明

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は動作しないため修正としては不十分かもしれない。


関連するチケット 5 (2件未完了3件完了)

関連している OpenPNE 3 - Bug(バグ) #3052: activity/community.json 実行時にDoctrineでエラーが発生する場合があるWon't fix(対応せず)Rimpei Ogawa2012-05-10

操作
関連している OpenPNE 3 - Bug(バグ) #991: [Optimization] Doctrine_Query::andWhereIn() add inefficient conditions to a DQL (Doctrine_Query::andWhereIn() が非効率な条件を DQL に追加する)Fixed(完了)Kousuke Ebihara2010-04-27

操作
関連している OpenPNE 3 - Bug(バグ) #3273: activity/search.json?member_id=? で特定メンバーのアクティビティを取得すると500エラーとなるFixed(完了)kaoru n2012-12-06

操作
関連している OpenPNE 3 - Bug(バグ) #3338: Doctrine のサブクエリの中でエイリアスを指定した場合に正しくないSQLが発行される場合があるNew(新規)2013-05-07

操作
関連している OpenPNE 3 - Bug(バグ) #3339: Doctrine で WHERE IN を用いたサブクエリの場合に配列が正しく展開されないNew(新規)2013-05-07

操作

他の形式にエクスポート: Atom PDF