Commit 593d5bb8 authored by zYne's avatar zYne

DQL LIMIT improved

parent ee4b46ad
......@@ -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) {
......
Following data types are availible in doctrine:
<ul>
<li /><b> string / s</b>
<li /><b> string </b>
<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 />
<li /><b> integer / int / i</b>
<li /><b> integer</b>
<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 />
<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>
<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>
<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
can be specified with Doctrine_Record::setEnumValues(columnName, array values).</ul>
<li /><b> timestamp / t</b>
<li /><b> timestamp </b>
<dd /> Database 'timestamp' type
<li /><b> clob</b>
<dd /> Database 'clob' type
<li /><b> blob</b>
<dd /> Database 'blob' type
<li /><b> date / d</b>
<li /><b> date </b>
<dd /> Database 'date' type
</ul>
......@@ -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.
<br \>
<br \>
<?php
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);
$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);
}
}
?>
......@@ -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());
......
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