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 {
}
/**
* getComponentAliases
*
* @return array
*/
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
*
......@@ -204,7 +247,7 @@ abstract class Doctrine_Hydrate extends Doctrine_Access {
* @param string $path
* @return string
*/
final public function getTableAlias($path) {
final public function getTableAlias($path) {
if(isset($this->compAliases[$path]))
$path = $this->compAliases[$path];
......@@ -524,5 +567,11 @@ abstract class Doctrine_Hydrate extends Doctrine_Access {
public function getTable($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 {
* parsers for each part
*
* @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
* @return Doctrine_Query
*/
public function parseQuery($query) {
$this->clear();
public function parseQuery($query, $clear = true) {
if($clear)
$this->clear();
$query = trim($query);
$query = str_replace("\n"," ",$query);
......
......@@ -10,9 +10,19 @@ class Doctrine_Query_Where extends Doctrine_Query_Condition {
* @return string
*/
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) {
$e = Doctrine_Query::sqlExplode($where, array('=', '<', '>', '!='));
}
......@@ -84,9 +94,24 @@ class Doctrine_Query_Where extends Doctrine_Query_Condition {
elseif($value == 'false')
$value = 0;
elseif(substr($value,0,5) == '(FROM') {
// subquery
$sub = Doctrine_Query::bracketTrim($value);
$q = new Doctrine_Query();
$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) {
......@@ -95,18 +120,27 @@ class Doctrine_Query_Where extends Doctrine_Query_Condition {
case '=':
if($enumIndex !== false)
$value = $enumIndex;
$where = $alias.'.'.$field.' '.$operator.' '.$value;
break;
default:
$where = $this->query->getTableAlias($reference).'.'.$field.' '.$operator.' '.$value;
$where = $alias.'.'.$field.' '.$operator.' '.$value;
}
}
}
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) {
switch($func) {
case 'contains':
......
......@@ -29,7 +29,7 @@ class Doctrine_Query_Where_TestCase extends Doctrine_UnitTestCase {
$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();
......@@ -37,17 +37,78 @@ class Doctrine_Query_Where_TestCase extends Doctrine_UnitTestCase {
$this->assertEqual($users[0]->name, 'someone');
$this->assertEqual($users[1]->name, 'someone.2');
}
public function testComponentAliases() {
public function testNotInExpression() {
$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->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();
$this->assertEqual($users->count(), 2);
$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() {
$q = new Doctrine_Query();
......@@ -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)");
}
}
?>
......@@ -110,21 +110,23 @@ $test->addTestCase(new Doctrine_RelationAccessTestCase());
$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_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_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_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