From 593d5bb84726a74be19e2bf2a2c6b77e4d080453 Mon Sep 17 00:00:00 2001 From: zYne Date: Wed, 16 Aug 2006 09:17:43 +0000 Subject: [PATCH] DQL LIMIT improved --- Doctrine/Query.php | 52 +++++++++--- ...le definition - Data types and lengths.php | 22 +++-- tests/QueryLimitTestCase.php | 81 ++++++++++++++++++- tests/run.php | 6 +- 4 files changed, 131 insertions(+), 30 deletions(-) diff --git a/Doctrine/Query.php b/Doctrine/Query.php index edb099aab..806c10b1a 100644 --- a/Doctrine/Query.php +++ b/Doctrine/Query.php @@ -27,6 +27,14 @@ require_once("Hydrate.php"); * @license LGPL */ class Doctrine_Query extends Doctrine_Hydrate { + /** + * @param array $subqueryAliases the table aliases needed in some LIMIT subqueries + */ + private $subqueryAliases = array(); + /** + * @param boolean $needsSubquery + */ + private $needsSubquery = false; /** * count * @@ -246,7 +254,7 @@ class Doctrine_Query extends Doctrine_Hydrate { $needsSubQuery = false; $subquery = ''; - if( ! empty($this->parts['limit'])) + if( ! empty($this->parts['limit']) && $this->needsSubquery) $needsSubQuery = true; // build the basic query @@ -261,7 +269,7 @@ class Doctrine_Query extends Doctrine_Hydrate { $table = $this->tables[$k[0]]; if($needsSubQuery) - $subquery = 'SELECT '.$table->getTableName().".".$table->getIdentifier(). + $subquery = 'SELECT DISTINCT '.$table->getTableName().".".$table->getIdentifier(). ' FROM '.$table->getTableName(); if( ! empty($this->parts['join'])) { @@ -272,14 +280,19 @@ class Doctrine_Query extends Doctrine_Hydrate { if($needsSubQuery) { foreach($this->parts['join'] as $parts) { foreach($parts as $part) { - if(substr($part,0,9) !== 'LEFT JOIN') - $subquery .= " ".$part; + if(substr($part,0,9) === 'LEFT JOIN') { + $e = explode(' ', $part); + + if( ! in_array($e[2],$this->subqueryAliases)) + continue; + } + + $subquery .= " ".$part; } } } } - $string = $this->applyInheritance(); if( ! empty($string)) @@ -292,17 +305,23 @@ class Doctrine_Query extends Doctrine_Hydrate { $subquery .= ( ! empty($this->parts['orderby']))?" ORDER BY ".implode(" ",$this->parts["orderby"]):''; } - if( ! empty($this->parts["limit"]) || ! empty($this->parts["offset"]) && $needsSubQuery) { - $subquery = $this->session->modifyLimitQuery($subquery,$this->parts["limit"],$this->parts["offset"]); - - $field = $table->getTableName().'.'.$table->getIdentifier(); - array_unshift($this->parts['where'], $field.' IN ('.$subquery.')'); + $modifyLimit = false; + if( ! empty($this->parts["limit"]) || ! empty($this->parts["offset"])) { + if($needsSubQuery) { + $subquery = $this->session->modifyLimitQuery($subquery,$this->parts["limit"],$this->parts["offset"]); + + $field = $table->getTableName().'.'.$table->getIdentifier(); + array_unshift($this->parts['where'], $field.' IN ('.$subquery.')'); + } else + $modifyLimit = true; } $q .= ( ! empty($this->parts['where']))?" WHERE ".implode(" AND ",$this->parts["where"]):''; $q .= ( ! empty($this->parts['groupby']))?" GROUP BY ".implode(", ",$this->parts["groupby"]):''; $q .= ( ! empty($this->parts['having']))?" HAVING ".implode(" ",$this->parts["having"]):''; $q .= ( ! empty($this->parts['orderby']))?" ORDER BY ".implode(" ",$this->parts["orderby"]):''; + if($modifyLimit) + $q = $this->session->modifyLimitQuery($q,$this->parts["limit"],$this->parts["offset"]); // return to the previous state if( ! empty($string)) @@ -545,7 +564,7 @@ class Doctrine_Query extends Doctrine_Hydrate { $table = $this->session->getTable($name); $tname = $table->getTableName(); - + if( ! isset($this->tableAliases[$currPath])) $this->tableIndexes[$tname] = 1; @@ -569,7 +588,9 @@ class Doctrine_Query extends Doctrine_Hydrate { $fk = $table->getForeignKey($name); $name = $fk->getTable()->getComponentName(); $original = $fk->getTable()->getTableName(); - + + + if(isset($this->tableAliases[$currPath])) { $tname2 = $this->tableAliases[$currPath]; } else @@ -591,6 +612,13 @@ class Doctrine_Query extends Doctrine_Hydrate { throw new Doctrine_Exception("Unknown operator '$mark'"); endswitch; + if($fk->getType() == Doctrine_Relation::MANY_AGGREGATE || + $fk->getType() == Doctrine_Relation::MANY_COMPOSITE) { + if( ! $loadFields) + $this->subqueryAliases[] = $tname2; + + $this->needsSubquery = true; + } if($fk instanceof Doctrine_ForeignKey || $fk instanceof Doctrine_LocalKey) { diff --git a/manual/docs/Getting started - Setting table definition - Data types and lengths.php b/manual/docs/Getting started - Setting table definition - Data types and lengths.php index ffe066415..97b5772ac 100644 --- a/manual/docs/Getting started - Setting table definition - Data types and lengths.php +++ b/manual/docs/Getting started - Setting table definition - Data types and lengths.php @@ -1,27 +1,27 @@ Following data types are availible in doctrine: @@ -36,6 +36,4 @@ However when the record is validated it is only allowed to have 'content' -colum In general Doctrine is smart enough to know which integer/string type to use depending on the specified length. -
- - +
diff --git a/tests/QueryLimitTestCase.php b/tests/QueryLimitTestCase.php index e4415137b..f2281e765 100644 --- a/tests/QueryLimitTestCase.php +++ b/tests/QueryLimitTestCase.php @@ -1,12 +1,80 @@ query->from("User.Phonenumber"); + + public function testLimitWithOneToOneLeftJoin() { + $q = new Doctrine_Query($this->session); + $q->from('User(id).Email')->limit(5); + + $users = $q->execute(); + $this->assertEqual($users->count(), 5); + + } + public function testLimitWithOneToOneInnerJoin() { + $q = new Doctrine_Query($this->session); + $q->from('User(id):Email')->limit(5); + + $users = $q->execute(); + $this->assertEqual($users->count(), 5); + } + public function testLimitWithOneToManyLeftJoin() { + $this->query->from("User(id).Phonenumber"); $this->query->limit(5); $sql = $this->query->getQuery(); + $users = $this->query->execute(); + $count = $this->dbh->count(); + $this->assertEqual($users->count(), 5); + $users[0]->Phonenumber[0]; + $this->assertEqual($count, $this->dbh->count()); + $this->assertEqual($this->query->getQuery(), + 'SELECT entity.id AS entity__id, phonenumber.id AS phonenumber__id, phonenumber.phonenumber AS phonenumber__phonenumber, phonenumber.entity_id AS phonenumber__entity_id FROM entity LEFT JOIN phonenumber ON entity.id = phonenumber.entity_id WHERE entity.id IN (SELECT DISTINCT entity.id FROM entity WHERE (entity.type = 0) LIMIT 5) AND (entity.type = 0)'); + + + $this->query->offset(2); + + $users = $this->query->execute(); + $count = $this->dbh->count(); + $this->assertEqual($users->count(), 5); + $users[3]->Phonenumber[0]; + $this->assertEqual($count, $this->dbh->count()); + } + public function testLimitWithOneToManyLeftJoinAndCondition() { + $q = new Doctrine_Query($this->session); + $q->from("User(name)")->where("User.Phonenumber.phonenumber LIKE '%123%'")->limit(5); + $users = $q->execute(); + $this->assertEqual($users[0]->name, 'zYne'); + $this->assertEqual($users[1]->name, 'Arnold Schwarzenegger'); + $this->assertEqual($users[2]->name, 'Michael Caine'); + $this->assertEqual($users[3]->name, 'Sylvester Stallone'); + $this->assertEqual($users[4]->name, 'Jean Reno'); + + $this->assertEqual($users->count(), 5); + $this->assertEqual($q->getQuery(), + "SELECT entity.id AS entity__id, entity.name AS entity__name FROM entity LEFT JOIN phonenumber ON entity.id = phonenumber.entity_id WHERE entity.id IN (SELECT DISTINCT entity.id FROM entity LEFT JOIN phonenumber ON entity.id = phonenumber.entity_id WHERE phonenumber.phonenumber LIKE '%123%' AND (entity.type = 0) LIMIT 5) AND phonenumber.phonenumber LIKE '%123%' AND (entity.type = 0)"); + } + + public function testLimitWithOneToManyLeftJoinAndOrderBy() { + $q = new Doctrine_Query($this->session); + $q->from("User(name)")->where("User.Phonenumber.phonenumber LIKE '%123%'")->orderby("User.Email.address")->limit(5); + $users = $q->execute(); + + $this->assertEqual($users[0]->name, 'Arnold Schwarzenegger'); + $this->assertEqual($users[1]->name, 'Michael Caine'); + $this->assertEqual($users[2]->name, 'Jean Reno'); + $this->assertEqual($users[3]->name, 'Sylvester Stallone'); + $this->assertEqual($users[4]->name, 'zYne'); + + $this->assertEqual($users->count(), 5); + } + + + public function testLimitWithOneToManyInnerJoin() { + $this->query->from("User(id):Phonenumber"); + $this->query->limit(5); + + $sql = $this->query->getQuery(); $users = $this->query->execute(); $count = $this->dbh->count(); @@ -22,6 +90,15 @@ class Doctrine_Query_Limit_TestCase extends Doctrine_UnitTestCase { $this->assertEqual($users->count(), 5); $users[3]->Phonenumber[0]; $this->assertEqual($count, $this->dbh->count()); + + $this->assertEqual($this->query->getQuery(), + 'SELECT entity.id AS entity__id, phonenumber.id AS phonenumber__id, phonenumber.phonenumber AS phonenumber__phonenumber, phonenumber.entity_id AS phonenumber__entity_id FROM entity INNER JOIN phonenumber ON entity.id = phonenumber.entity_id WHERE entity.id IN (SELECT DISTINCT entity.id FROM entity INNER JOIN phonenumber ON entity.id = phonenumber.entity_id WHERE (entity.type = 0) LIMIT 5 OFFSET 2) AND (entity.type = 0)'); } + + public function testLimitWithManyToManyLeftJoin() { + $q = new Doctrine_Query($this->session); + $q->from("User.Group")->limit(5); + } + } ?> diff --git a/tests/run.php b/tests/run.php index 007cc8bf9..36b319269 100644 --- a/tests/run.php +++ b/tests/run.php @@ -28,7 +28,7 @@ require_once("QueryLimitTestCase.php"); error_reporting(E_ALL); $test = new GroupTest("Doctrine Framework Unit Tests"); - +/** $test->addTestCase(new Doctrine_RecordTestCase()); $test->addTestCase(new Doctrine_SessionTestCase()); @@ -53,8 +53,6 @@ $test->addTestCase(new Doctrine_ViewTestCase()); $test->addTestCase(new Doctrine_Cache_Query_SqliteTestCase()); - - $test->addTestCase(new Doctrine_RawSql_TestCase()); $test->addTestCase(new Doctrine_CustomPrimaryKeyTestCase()); @@ -68,7 +66,7 @@ $test->addTestCase(new Doctrine_ValidatorTestCase()); $test->addTestCase(new Doctrine_CollectionTestCase()); $test->addTestCase(new Doctrine_QueryTestCase()); - +*/ $test->addTestCase(new Doctrine_Query_Limit_TestCase()); //$test->addTestCase(new Doctrine_Cache_FileTestCase());