Commit 8b4bc0f4 authored by zYne's avatar zYne

Fixes #159, #160, added EXISTS expression support as well as correlated subquery support

parent db25f46e
...@@ -109,11 +109,54 @@ abstract class Doctrine_Hydrate extends Doctrine_Access { ...@@ -109,11 +109,54 @@ abstract class Doctrine_Hydrate extends Doctrine_Access {
} }
/** /**
* getComponentAliases * getComponentAliases
*
* @return array
*/ */
public function getComponentAliases() { public function getComponentAliases() {
return $this->compAliases; return $this->compAliases;
}
/**
* getTableAliases
*
* @return array
*/
public function getTableAliases() {
return $this->tableAliases;
}
/**
* getTableIndexes
*
* @return array
*/
public function getTableIndexes() {
return $this->tableIndexes;
}
/**
* copyAliases
*
* @return void
*/
public function copyAliases(Doctrine_Hydrate $query) {
$this->compAliases = $query->getComponentAliases();
$this->tableAliases = $query->getTableAliases();
$this->tableIndexes = $query->getTableIndexes();
return $this;
} }
/**
* createSubquery
*
* @return Doctrine_Hydrate
*/
public function createSubquery() {
$class = get_class($this);
$obj = new $class();
// copy the aliases to the subquery
$obj->copyAliases($this);
return $obj;
}
/** /**
* getQuery * getQuery
* *
...@@ -204,7 +247,7 @@ abstract class Doctrine_Hydrate extends Doctrine_Access { ...@@ -204,7 +247,7 @@ abstract class Doctrine_Hydrate extends Doctrine_Access {
* @param string $path * @param string $path
* @return string * @return string
*/ */
final public function getTableAlias($path) { final public function getTableAlias($path) {
if(isset($this->compAliases[$path])) if(isset($this->compAliases[$path]))
$path = $this->compAliases[$path]; $path = $this->compAliases[$path];
...@@ -524,5 +567,11 @@ abstract class Doctrine_Hydrate extends Doctrine_Access { ...@@ -524,5 +567,11 @@ abstract class Doctrine_Hydrate extends Doctrine_Access {
public function getTable($name) { public function getTable($name) {
return $this->tables[$name]; return $this->tables[$name];
} }
/**
* @return string returns a string representation of this object
*/
public function __toString() {
return Doctrine_Lib::formatSql($this->getQuery());
}
} }
...@@ -424,11 +424,13 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable { ...@@ -424,11 +424,13 @@ class Doctrine_Query extends Doctrine_Hydrate implements Countable {
* parsers for each part * parsers for each part
* *
* @param string $query DQL query * @param string $query DQL query
* @param boolean $clear whether or not to clear the aliases
* @throws Doctrine_Query_Exception if some generic parsing error occurs * @throws Doctrine_Query_Exception if some generic parsing error occurs
* @return Doctrine_Query * @return Doctrine_Query
*/ */
public function parseQuery($query) { public function parseQuery($query, $clear = true) {
$this->clear(); if($clear)
$this->clear();
$query = trim($query); $query = trim($query);
$query = str_replace("\n"," ",$query); $query = str_replace("\n"," ",$query);
......
...@@ -10,9 +10,19 @@ class Doctrine_Query_Where extends Doctrine_Query_Condition { ...@@ -10,9 +10,19 @@ class Doctrine_Query_Where extends Doctrine_Query_Condition {
* @return string * @return string
*/ */
public function load($where) { public function load($where) {
$where = trim($where);
$e = Doctrine_Query::sqlExplode($where);
if(count($e) > 1) {
$tmp = $e[0].' '.$e[1];
if(substr($tmp, 0, 6) == 'EXISTS')
return $this->parseExists($where, true);
elseif(substr($where, 0, 10) == 'NOT EXISTS')
return $this->parseExists($where, false);
}
$e = Doctrine_Query::sqlExplode($where);
if(count($e) < 3) { if(count($e) < 3) {
$e = Doctrine_Query::sqlExplode($where, array('=', '<', '>', '!=')); $e = Doctrine_Query::sqlExplode($where, array('=', '<', '>', '!='));
} }
...@@ -84,9 +94,24 @@ class Doctrine_Query_Where extends Doctrine_Query_Condition { ...@@ -84,9 +94,24 @@ class Doctrine_Query_Where extends Doctrine_Query_Condition {
elseif($value == 'false') elseif($value == 'false')
$value = 0; $value = 0;
elseif(substr($value,0,5) == '(FROM') { elseif(substr($value,0,5) == '(FROM') {
// subquery
$sub = Doctrine_Query::bracketTrim($value); $sub = Doctrine_Query::bracketTrim($value);
$q = new Doctrine_Query(); $q = new Doctrine_Query();
$value = '(' . $q->parseQuery($sub)->getQuery() . ')'; $value = '(' . $q->parseQuery($sub)->getQuery() . ')';
} else {
// check that value isn't a string
if(strpos($value, '\'') === false) {
$a = explode('.', $value);
if(count($a) > 1) {
// either a float or a component..
if( ! is_numeric($a[0])) {
// a component found
$value = $this->query->getTableAlias($a[0]). '.' . $a[1];
}
}
}
} }
switch($operator) { switch($operator) {
...@@ -95,18 +120,27 @@ class Doctrine_Query_Where extends Doctrine_Query_Condition { ...@@ -95,18 +120,27 @@ class Doctrine_Query_Where extends Doctrine_Query_Condition {
case '=': case '=':
if($enumIndex !== false) if($enumIndex !== false)
$value = $enumIndex; $value = $enumIndex;
$where = $alias.'.'.$field.' '.$operator.' '.$value;
break;
default: default:
$where = $this->query->getTableAlias($reference).'.'.$field.' '.$operator.' '.$value; $where = $alias.'.'.$field.' '.$operator.' '.$value;
} }
} }
} }
return $where; return $where;
} }
public function parseExists($where, $negation) {
$operator = ($negation) ? 'EXISTS' : 'NOT EXISTS';
$pos = strpos($where, '(');
if($pos == false)
throw new Doctrine_Query_Exception("Unknown expression, expected '('");
$sub = Doctrine_Query::bracketTrim(substr($where, $pos));
return $operator . ' ('.$this->query->createSubquery()->parseQuery($sub, false)->getQuery() . ')';
}
public function getOperator($func) { public function getOperator($func) {
switch($func) { switch($func) {
case 'contains': case 'contains':
......
...@@ -29,7 +29,7 @@ class Doctrine_Query_Where_TestCase extends Doctrine_UnitTestCase { ...@@ -29,7 +29,7 @@ class Doctrine_Query_Where_TestCase extends Doctrine_UnitTestCase {
$q = new Doctrine_Query(); $q = new Doctrine_Query();
$q->from('User(id)')->addWhere('User.id IN (?, ?)',array(1,2)); $q->from('User(id)')->addWhere('User.id IN (?, ?)', array(1,2));
$users = $q->execute(); $users = $q->execute();
...@@ -37,17 +37,78 @@ class Doctrine_Query_Where_TestCase extends Doctrine_UnitTestCase { ...@@ -37,17 +37,78 @@ class Doctrine_Query_Where_TestCase extends Doctrine_UnitTestCase {
$this->assertEqual($users[0]->name, 'someone'); $this->assertEqual($users[0]->name, 'someone');
$this->assertEqual($users[1]->name, 'someone.2'); $this->assertEqual($users[1]->name, 'someone.2');
} }
public function testComponentAliases() {
public function testNotInExpression() {
$q = new Doctrine_Query(); $q = new Doctrine_Query();
$q->from('User u')->addWhere('u.id NOT IN (?)', array(1));
$users = $q->execute();
$this->assertEqual($users->count(), 1);
$this->assertEqual($users[0]->name, 'someone.2');
}
public function testExistsExpression() {
$q = new Doctrine_Query();
$user = new User();
$user->name = 'someone with a group';
$user->Group[0]->name = 'some group';
$user->save();
// find all users which have groups
try {
$q->from('User u')->where('EXISTS (FROM Groupuser(id) WHERE Groupuser.user_id = u.id)');
$this->pass();
} catch(Doctrine_Exception $e) {
$this->fail();
}
$users = $q->execute();
$this->assertEqual($users->count(), 1);
$this->assertEqual($users[0]->name, 'someone with a group');
}
public function testNotExistsExpression() {
$q = new Doctrine_Query();
// find all users which don't have groups
try {
$q->from('User u')->where('NOT EXISTS (FROM Groupuser(id) WHERE Groupuser.user_id = u.id)');
$this->pass();
} catch(Doctrine_Exception $e) {
$this->fail();
}
$users = $q->execute();
$this->assertEqual($users->count(), 2);
$this->assertEqual($users[0]->name, 'someone');
$this->assertEqual($users[1]->name, 'someone.2');
}
public function testComponentAliases() {
$q = new Doctrine_Query(); $q = new Doctrine_Query();
$q->from('User(id) u')->addWhere('u.id IN (?, ?)',array(1,2)); $q->from('User(id) u')->addWhere('u.id IN (?, ?)', array(1,2));
$users = $q->execute(); $users = $q->execute();
$this->assertEqual($users->count(), 2); $this->assertEqual($users->count(), 2);
$this->assertEqual($users[0]->name, 'someone'); $this->assertEqual($users[0]->name, 'someone');
$this->assertEqual($users[1]->name, 'someone.2'); $this->assertEqual($users[1]->name, 'someone.2');
}
public function testComponentAliases2() {
$q = new Doctrine_Query();
$q->from('User u')->addWhere('u.name = ?', array('someone'));
$users = $q->execute();
$this->assertEqual($users->count(), 1);
$this->assertEqual($users[0]->name, 'someone');
}
public function testComponentAliases3() {
$users = $this->connection->query("FROM User u WHERE u.name = ?", array('someone'));
$this->assertEqual($users->count(), 1);
$this->assertEqual($users[0]->name, 'someone');
} }
public function testOperatorWithNoTrailingSpaces() { public function testOperatorWithNoTrailingSpaces() {
$q = new Doctrine_Query(); $q = new Doctrine_Query();
...@@ -89,5 +150,6 @@ class Doctrine_Query_Where_TestCase extends Doctrine_UnitTestCase { ...@@ -89,5 +150,6 @@ class Doctrine_Query_Where_TestCase extends Doctrine_UnitTestCase {
$this->assertEqual($q->getQuery(), "SELECT entity.id AS entity__id FROM entity WHERE entity.name = 'foo.bar' AND (entity.type = 0)"); $this->assertEqual($q->getQuery(), "SELECT entity.id AS entity__id FROM entity WHERE entity.name = 'foo.bar' AND (entity.type = 0)");
} }
} }
?> ?>
...@@ -110,21 +110,23 @@ $test->addTestCase(new Doctrine_RelationAccessTestCase()); ...@@ -110,21 +110,23 @@ $test->addTestCase(new Doctrine_RelationAccessTestCase());
$test->addTestCase(new Doctrine_CustomResultSetOrderTestCase()); $test->addTestCase(new Doctrine_CustomResultSetOrderTestCase());
$test->addTestCase(new Doctrine_QueryTestCase());
$test->addTestCase(new Doctrine_Query_Where_TestCase());
$test->addTestCase(new Doctrine_Query_Condition_TestCase());
$test->addTestCase(new Doctrine_BooleanTestCase()); $test->addTestCase(new Doctrine_BooleanTestCase());
$test->addTestCase(new Doctrine_EnumTestCase()); $test->addTestCase(new Doctrine_EnumTestCase());
$test->addTestCase(new Doctrine_Record_Filter_TestCase());
$test->addTestCase(new Doctrine_Query_Condition_TestCase());
$test->addTestCase(new Doctrine_Query_ComponentAlias_TestCase()); $test->addTestCase(new Doctrine_Query_ComponentAlias_TestCase());
$test->addTestCase(new Doctrine_Query_Subquery_TestCase()); $test->addTestCase(new Doctrine_Query_Subquery_TestCase());
$test->addTestCase(new Doctrine_Record_Filter_TestCase()); $test->addTestCase(new Doctrine_QueryTestCase());
$test->addTestCase(new Doctrine_Query_Where_TestCase());
//$test->addTestCase(new Doctrine_Cache_FileTestCase()); //$test->addTestCase(new Doctrine_Cache_FileTestCase());
//$test->addTestCase(new Doctrine_Cache_SqliteTestCase()); //$test->addTestCase(new Doctrine_Cache_SqliteTestCase());
......
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