Commit 593d5bb8 authored by zYne's avatar zYne

DQL LIMIT improved

parent ee4b46ad
...@@ -27,6 +27,14 @@ require_once("Hydrate.php"); ...@@ -27,6 +27,14 @@ require_once("Hydrate.php");
* @license LGPL * @license LGPL
*/ */
class Doctrine_Query extends Doctrine_Hydrate { 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 * count
* *
...@@ -246,7 +254,7 @@ class Doctrine_Query extends Doctrine_Hydrate { ...@@ -246,7 +254,7 @@ class Doctrine_Query extends Doctrine_Hydrate {
$needsSubQuery = false; $needsSubQuery = false;
$subquery = ''; $subquery = '';
if( ! empty($this->parts['limit'])) if( ! empty($this->parts['limit']) && $this->needsSubquery)
$needsSubQuery = true; $needsSubQuery = true;
// build the basic query // build the basic query
...@@ -261,7 +269,7 @@ class Doctrine_Query extends Doctrine_Hydrate { ...@@ -261,7 +269,7 @@ class Doctrine_Query extends Doctrine_Hydrate {
$table = $this->tables[$k[0]]; $table = $this->tables[$k[0]];
if($needsSubQuery) if($needsSubQuery)
$subquery = 'SELECT '.$table->getTableName().".".$table->getIdentifier(). $subquery = 'SELECT DISTINCT '.$table->getTableName().".".$table->getIdentifier().
' FROM '.$table->getTableName(); ' FROM '.$table->getTableName();
if( ! empty($this->parts['join'])) { if( ! empty($this->parts['join'])) {
...@@ -272,14 +280,19 @@ class Doctrine_Query extends Doctrine_Hydrate { ...@@ -272,14 +280,19 @@ class Doctrine_Query extends Doctrine_Hydrate {
if($needsSubQuery) { if($needsSubQuery) {
foreach($this->parts['join'] as $parts) { foreach($this->parts['join'] as $parts) {
foreach($parts as $part) { foreach($parts as $part) {
if(substr($part,0,9) !== 'LEFT JOIN') if(substr($part,0,9) === 'LEFT JOIN') {
$e = explode(' ', $part);
if( ! in_array($e[2],$this->subqueryAliases))
continue;
}
$subquery .= " ".$part; $subquery .= " ".$part;
} }
} }
} }
} }
$string = $this->applyInheritance(); $string = $this->applyInheritance();
if( ! empty($string)) if( ! empty($string))
...@@ -292,17 +305,23 @@ class Doctrine_Query extends Doctrine_Hydrate { ...@@ -292,17 +305,23 @@ class Doctrine_Query extends Doctrine_Hydrate {
$subquery .= ( ! empty($this->parts['orderby']))?" ORDER BY ".implode(" ",$this->parts["orderby"]):''; $subquery .= ( ! empty($this->parts['orderby']))?" ORDER BY ".implode(" ",$this->parts["orderby"]):'';
} }
if( ! empty($this->parts["limit"]) || ! empty($this->parts["offset"]) && $needsSubQuery) { $modifyLimit = false;
if( ! empty($this->parts["limit"]) || ! empty($this->parts["offset"])) {
if($needsSubQuery) {
$subquery = $this->session->modifyLimitQuery($subquery,$this->parts["limit"],$this->parts["offset"]); $subquery = $this->session->modifyLimitQuery($subquery,$this->parts["limit"],$this->parts["offset"]);
$field = $table->getTableName().'.'.$table->getIdentifier(); $field = $table->getTableName().'.'.$table->getIdentifier();
array_unshift($this->parts['where'], $field.' IN ('.$subquery.')'); 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['where']))?" WHERE ".implode(" AND ",$this->parts["where"]):'';
$q .= ( ! empty($this->parts['groupby']))?" GROUP BY ".implode(", ",$this->parts["groupby"]):''; $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['having']))?" HAVING ".implode(" ",$this->parts["having"]):'';
$q .= ( ! empty($this->parts['orderby']))?" ORDER BY ".implode(" ",$this->parts["orderby"]):''; $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 // return to the previous state
if( ! empty($string)) if( ! empty($string))
...@@ -570,6 +589,8 @@ class Doctrine_Query extends Doctrine_Hydrate { ...@@ -570,6 +589,8 @@ class Doctrine_Query extends Doctrine_Hydrate {
$name = $fk->getTable()->getComponentName(); $name = $fk->getTable()->getComponentName();
$original = $fk->getTable()->getTableName(); $original = $fk->getTable()->getTableName();
if(isset($this->tableAliases[$currPath])) { if(isset($this->tableAliases[$currPath])) {
$tname2 = $this->tableAliases[$currPath]; $tname2 = $this->tableAliases[$currPath];
} else } else
...@@ -591,6 +612,13 @@ class Doctrine_Query extends Doctrine_Hydrate { ...@@ -591,6 +612,13 @@ class Doctrine_Query extends Doctrine_Hydrate {
throw new Doctrine_Exception("Unknown operator '$mark'"); throw new Doctrine_Exception("Unknown operator '$mark'");
endswitch; 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 || if($fk instanceof Doctrine_ForeignKey ||
$fk instanceof Doctrine_LocalKey) { $fk instanceof Doctrine_LocalKey) {
......
Following data types are availible in doctrine: Following data types are availible in doctrine:
<ul> <ul>
<li /><b> string / s</b> <li /><b> string </b>
<dd /> The same as type 'string' in php <dd /> The same as type 'string' in php
<li /><b> float / double / f</b> <li /><b> float / double</b>
<dd /> The same as type 'float' in php<br /> <dd /> The same as type 'float' in php<br />
<li /><b> integer / int / i</b> <li /><b> integer</b>
<dd /> The same as type 'integer' in php<br /> <dd /> The same as type 'integer' in php<br />
<li /><b> boolean / bool</b> <li /><b> boolean </b>
<dd /> The same as type 'boolean' in php<br /> <dd /> The same as type 'boolean' in php<br />
<li /><b> array / a</b> <li /><b> array </b>
<ul> The same as type 'array' in php. Automatically serialized when saved into database and unserialized when retrieved from database.</ul> <ul> The same as type 'array' in php. Automatically serialized when saved into database and unserialized when retrieved from database.</ul>
<li /><b> object / o</b> <li /><b> object </b>
<ul> The same as type 'object' in php. Automatically serialized when saved into database and unserialized when retrieved from database.</ul> <ul> The same as type 'object' in php. Automatically serialized when saved into database and unserialized when retrieved from database.</ul>
<li /><b> enum / e</b> <li /><b> enum </b>
<ul> Unified 'enum' type. Automatically converts the string values into index numbers and vice versa. The possible values for the column <ul> Unified 'enum' type. Automatically converts the string values into index numbers and vice versa. The possible values for the column
can be specified with Doctrine_Record::setEnumValues(columnName, array values).</ul> can be specified with Doctrine_Record::setEnumValues(columnName, array values).</ul>
<li /><b> timestamp / t</b> <li /><b> timestamp </b>
<dd /> Database 'timestamp' type <dd /> Database 'timestamp' type
<li /><b> clob</b> <li /><b> clob</b>
<dd /> Database 'clob' type <dd /> Database 'clob' type
<li /><b> blob</b> <li /><b> blob</b>
<dd /> Database 'blob' type <dd /> Database 'blob' type
<li /><b> date / d</b> <li /><b> date </b>
<dd /> Database 'date' type <dd /> Database 'date' type
</ul> </ul>
...@@ -37,5 +37,3 @@ However when the record is validated it is only allowed to have 'content' -colum ...@@ -37,5 +37,3 @@ 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. In general Doctrine is smart enough to know which integer/string type to use depending on the specified length.
<br \> <br \>
<?php <?php
class Doctrine_Query_Limit_TestCase extends Doctrine_UnitTestCase { class Doctrine_Query_Limit_TestCase extends Doctrine_UnitTestCase {
public function testLimit() {
$this->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); $this->query->limit(5);
$sql = $this->query->getQuery(); $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(); $users = $this->query->execute();
$count = $this->dbh->count(); $count = $this->dbh->count();
...@@ -22,6 +90,15 @@ class Doctrine_Query_Limit_TestCase extends Doctrine_UnitTestCase { ...@@ -22,6 +90,15 @@ class Doctrine_Query_Limit_TestCase extends Doctrine_UnitTestCase {
$this->assertEqual($users->count(), 5); $this->assertEqual($users->count(), 5);
$users[3]->Phonenumber[0]; $users[3]->Phonenumber[0];
$this->assertEqual($count, $this->dbh->count()); $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);
}
} }
?> ?>
...@@ -28,7 +28,7 @@ require_once("QueryLimitTestCase.php"); ...@@ -28,7 +28,7 @@ require_once("QueryLimitTestCase.php");
error_reporting(E_ALL); error_reporting(E_ALL);
$test = new GroupTest("Doctrine Framework Unit Tests"); $test = new GroupTest("Doctrine Framework Unit Tests");
/**
$test->addTestCase(new Doctrine_RecordTestCase()); $test->addTestCase(new Doctrine_RecordTestCase());
$test->addTestCase(new Doctrine_SessionTestCase()); $test->addTestCase(new Doctrine_SessionTestCase());
...@@ -53,8 +53,6 @@ $test->addTestCase(new Doctrine_ViewTestCase()); ...@@ -53,8 +53,6 @@ $test->addTestCase(new Doctrine_ViewTestCase());
$test->addTestCase(new Doctrine_Cache_Query_SqliteTestCase()); $test->addTestCase(new Doctrine_Cache_Query_SqliteTestCase());
$test->addTestCase(new Doctrine_RawSql_TestCase()); $test->addTestCase(new Doctrine_RawSql_TestCase());
$test->addTestCase(new Doctrine_CustomPrimaryKeyTestCase()); $test->addTestCase(new Doctrine_CustomPrimaryKeyTestCase());
...@@ -68,7 +66,7 @@ $test->addTestCase(new Doctrine_ValidatorTestCase()); ...@@ -68,7 +66,7 @@ $test->addTestCase(new Doctrine_ValidatorTestCase());
$test->addTestCase(new Doctrine_CollectionTestCase()); $test->addTestCase(new Doctrine_CollectionTestCase());
$test->addTestCase(new Doctrine_QueryTestCase()); $test->addTestCase(new Doctrine_QueryTestCase());
*/
$test->addTestCase(new Doctrine_Query_Limit_TestCase()); $test->addTestCase(new Doctrine_Query_Limit_TestCase());
//$test->addTestCase(new Doctrine_Cache_FileTestCase()); //$test->addTestCase(new Doctrine_Cache_FileTestCase());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment