DQL LIMIT improved
This commit is contained in:
parent
ee4b46adea
commit
593d5bb847
@ -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 {
|
|||||||
$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 {
|
|||||||
$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 {
|
|||||||
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') {
|
||||||
$subquery .= " ".$part;
|
$e = explode(' ', $part);
|
||||||
|
|
||||||
|
if( ! in_array($e[2],$this->subqueryAliases))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$subquery .= " ".$part;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
$string = $this->applyInheritance();
|
$string = $this->applyInheritance();
|
||||||
|
|
||||||
if( ! empty($string))
|
if( ! empty($string))
|
||||||
@ -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;
|
||||||
$subquery = $this->session->modifyLimitQuery($subquery,$this->parts["limit"],$this->parts["offset"]);
|
if( ! empty($this->parts["limit"]) || ! empty($this->parts["offset"])) {
|
||||||
|
if($needsSubQuery) {
|
||||||
$field = $table->getTableName().'.'.$table->getIdentifier();
|
$subquery = $this->session->modifyLimitQuery($subquery,$this->parts["limit"],$this->parts["offset"]);
|
||||||
array_unshift($this->parts['where'], $field.' IN ('.$subquery.')');
|
|
||||||
|
$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['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))
|
||||||
@ -545,7 +564,7 @@ class Doctrine_Query extends Doctrine_Hydrate {
|
|||||||
$table = $this->session->getTable($name);
|
$table = $this->session->getTable($name);
|
||||||
|
|
||||||
$tname = $table->getTableName();
|
$tname = $table->getTableName();
|
||||||
|
|
||||||
if( ! isset($this->tableAliases[$currPath]))
|
if( ! isset($this->tableAliases[$currPath]))
|
||||||
$this->tableIndexes[$tname] = 1;
|
$this->tableIndexes[$tname] = 1;
|
||||||
|
|
||||||
@ -569,7 +588,9 @@ class Doctrine_Query extends Doctrine_Hydrate {
|
|||||||
$fk = $table->getForeignKey($name);
|
$fk = $table->getForeignKey($name);
|
||||||
$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 {
|
|||||||
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) {
|
||||||
|
@ -1,27 +1,27 @@
|
|||||||
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>
|
||||||
|
|
||||||
@ -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.
|
In general Doctrine is smart enough to know which integer/string type to use depending on the specified length.
|
||||||
|
|
||||||
<br \>
|
<br \>
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,12 +1,80 @@
|
|||||||
<?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 {
|
|||||||
$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");
|
|||||||
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());
|
|||||||
|
|
||||||
$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());
|
|||||||
$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());
|
||||||
|
Loading…
x
Reference in New Issue
Block a user