DQL LIMIT improved
This commit is contained in:
parent
ee4b46adea
commit
593d5bb847
@ -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) {
|
||||
|
@ -1,27 +1,27 @@
|
||||
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 \>
|
||||
|
@ -1,12 +1,80 @@
|
||||
<?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());
|
||||
|
Loading…
x
Reference in New Issue
Block a user