[2.0] Implemented SQL generation for SIZE() function and EmptyCollectionComparisonExpression.
This commit is contained in:
parent
fde6431d1d
commit
f64347d899
@ -552,9 +552,6 @@ class EntityManager
|
||||
case Query::HYDRATE_SINGLE_SCALAR:
|
||||
$this->_hydrators[$hydrationMode] = new Internal\Hydration\SingleScalarHydrator($this);
|
||||
break;
|
||||
case Query::HYDRATE_NONE:
|
||||
$this->_hydrators[$hydrationMode] = new Internal\Hydration\NoneHydrator($this);
|
||||
break;
|
||||
default:
|
||||
throw DoctrineException::updateMe("No hydrator found for hydration mode '$hydrationMode'.");
|
||||
}
|
||||
|
@ -1,7 +1,22 @@
|
||||
<?php
|
||||
/*
|
||||
* To change this template, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
/*
|
||||
* $Id$
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* This software consists of voluntary contributions made by many individuals
|
||||
* and is licensed under the LGPL. For more information, see
|
||||
* <http://www.doctrine-project.org>.
|
||||
*/
|
||||
|
||||
namespace Doctrine\ORM\Query\AST\Functions;
|
||||
@ -9,7 +24,7 @@ namespace Doctrine\ORM\Query\AST\Functions;
|
||||
/**
|
||||
* "SIZE" "(" CollectionValuedPathExpression ")"
|
||||
*
|
||||
* @author robo
|
||||
* @author Roman Borschel <roman@code-factory.org>
|
||||
*/
|
||||
class SizeFunction extends FunctionNode
|
||||
{
|
||||
@ -19,14 +34,44 @@ class SizeFunction extends FunctionNode
|
||||
{
|
||||
return $this->_collectionPathExpression;
|
||||
}
|
||||
|
||||
public function setCollectionPathExpression($collPathExpr)
|
||||
{
|
||||
$this->_collectionPathExpression = $collPathExpr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker)
|
||||
{
|
||||
//TODO: Construct appropriate SQL
|
||||
return "";
|
||||
$dqlAlias = $this->_collectionPathExpression->getIdentificationVariable();
|
||||
$qComp = $sqlWalker->getQueryComponent($dqlAlias);
|
||||
$parts = $this->_collectionPathExpression->getParts();
|
||||
|
||||
$assoc = $qComp['metadata']->associationMappings[$parts[0]];
|
||||
|
||||
if ($assoc->isOneToMany()) {
|
||||
$targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc->targetEntityName);
|
||||
$targetAssoc = $targetClass->associationMappings[$assoc->mappedByFieldName];
|
||||
|
||||
$targetTableAlias = $sqlWalker->getSqlTableAlias($targetClass->primaryTable['name']);
|
||||
$sourceTableAlias = $sqlWalker->getSqlTableAlias($qComp['metadata']->primaryTable['name'], $dqlAlias);
|
||||
|
||||
$sql = "(SELECT COUNT($targetTableAlias."
|
||||
. implode(", $targetTableAlias.", $targetAssoc->targetToSourceKeyColumns)
|
||||
. ') FROM ' . $targetClass->primaryTable['name'] . ' ' . $targetTableAlias;
|
||||
|
||||
$whereSql = '';
|
||||
foreach ($targetAssoc->targetToSourceKeyColumns as $targetKeyColumn => $sourceKeyColumn) {
|
||||
if ($whereSql == '') $whereSql = ' WHERE '; else $whereSql .= ' AND ';
|
||||
$whereSql .= $targetTableAlias . '.' . $sourceKeyColumn . ' = ' . $sourceTableAlias . '.' . $targetKeyColumn;
|
||||
}
|
||||
|
||||
$sql .= $whereSql . ')';
|
||||
}
|
||||
|
||||
return $sql;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1970,7 +1970,7 @@ class Parser
|
||||
return $this->AggregateExpression();
|
||||
}
|
||||
|
||||
return $this->FunctionsReturningStrings();
|
||||
return $this->FunctionDeclaration();
|
||||
}
|
||||
|
||||
$this->syntaxError();
|
||||
|
@ -21,10 +21,8 @@
|
||||
|
||||
namespace Doctrine\ORM\Query;
|
||||
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\ORM\Query\Parser;
|
||||
use Doctrine\ORM\Query\AST;
|
||||
use Doctrine\Common\DoctrineException;
|
||||
use Doctrine\ORM\Query,
|
||||
Doctrine\Common\DoctrineException;
|
||||
|
||||
/**
|
||||
* The SqlWalker is a TreeWalker that walks over a DQL AST and constructs
|
||||
@ -124,6 +122,11 @@ class SqlWalker implements TreeWalker
|
||||
{
|
||||
return $this->_em;
|
||||
}
|
||||
|
||||
public function getQueryComponent($dqlAlias)
|
||||
{
|
||||
return $this->_queryComponents[$dqlAlias];
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down a SelectStatement AST node, thereby generating the appropriate SQL.
|
||||
@ -962,6 +965,7 @@ class SqlWalker implements TreeWalker
|
||||
$dqlParamKey = $entityExpr->isNamed() ? $entityExpr->getName() : $entityExpr->getPosition();
|
||||
$entity = $this->_query->getParameter($dqlParamKey);
|
||||
} else {
|
||||
//TODO
|
||||
throw DoctrineException::notImplemented();
|
||||
}
|
||||
|
||||
@ -1046,13 +1050,14 @@ class SqlWalker implements TreeWalker
|
||||
*
|
||||
* @param EmptyCollectionComparisonExpression
|
||||
* @return string The SQL.
|
||||
*
|
||||
* @todo Finish this implementation. It is quite incomplete!
|
||||
*/
|
||||
public function walkEmptyCollectionComparisonExpression($emptyCollCompExpr)
|
||||
{
|
||||
$sql = $this->walkPathExpression($emptyCollCompExpr->getExpression());
|
||||
$sql .= ' IS' . ($emptyCollCompExpr->isNot() ? ' NOT' : '') . ' EMPTY';
|
||||
$sizeFunc = new AST\Functions\SizeFunction('size');
|
||||
$sizeFunc->setCollectionPathExpression($emptyCollCompExpr->getExpression());
|
||||
|
||||
$sql = $sizeFunc->getSql($this);
|
||||
$sql .= ($emptyCollCompExpr->isNot() ? ' > 0' : ' = 0');
|
||||
|
||||
return $sql;
|
||||
}
|
||||
@ -1316,7 +1321,7 @@ class SqlWalker implements TreeWalker
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks down an PathExpression AST node, thereby generating the appropriate SQL.
|
||||
* Walks down a PathExpression AST node, thereby generating the appropriate SQL.
|
||||
*
|
||||
* @param mixed
|
||||
* @return string The SQL.
|
||||
@ -1324,8 +1329,9 @@ class SqlWalker implements TreeWalker
|
||||
public function walkPathExpression($pathExpr)
|
||||
{
|
||||
$sql = '';
|
||||
$pathExprType = $pathExpr->getType();
|
||||
|
||||
if ($pathExpr->getType() == AST\PathExpression::TYPE_STATE_FIELD) {
|
||||
if ($pathExprType == AST\PathExpression::TYPE_STATE_FIELD) {
|
||||
$parts = $pathExpr->getParts();
|
||||
$numParts = count($parts);
|
||||
$dqlAlias = $pathExpr->getIdentificationVariable();
|
||||
@ -1349,7 +1355,7 @@ class SqlWalker implements TreeWalker
|
||||
} else {
|
||||
$sql .= $this->_conn->quoteIdentifier($class->getColumnName($fieldName));
|
||||
}
|
||||
} else if ($pathExpr->isSimpleStateFieldAssociationPathExpression()) {
|
||||
} else if ($pathExprType == AST\PathExpression::TYPE_COLLECTION_VALUED_ASSOCIATION) {
|
||||
throw DoctrineException::updateMe("Not yet implemented.");
|
||||
} else {
|
||||
throw DoctrineException::updateMe("Encountered invalid PathExpression during SQL construction.");
|
||||
|
@ -330,6 +330,16 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase
|
||||
$this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :param MEMBER OF u.phonenumbers');
|
||||
}
|
||||
|
||||
public function testSizeFunction()
|
||||
{
|
||||
$this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE SIZE(u.phonenumbers) > 1');
|
||||
}
|
||||
|
||||
public function testEmptyCollectionComparisonExpression()
|
||||
{
|
||||
$this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.phonenumbers IS EMPTY');
|
||||
}
|
||||
|
||||
/**
|
||||
* This checks for invalid attempt to hydrate a proxy. It should throw an exception
|
||||
*
|
||||
|
@ -362,4 +362,24 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase
|
||||
|
||||
$this->assertEquals('SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ OFFSET 0 LIMIT 10', $q->getSql());
|
||||
}
|
||||
|
||||
public function testSizeFunction()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
"SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE SIZE(u.phonenumbers) > 1",
|
||||
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE (SELECT COUNT(c1_.user_id) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) > 1"
|
||||
);
|
||||
}
|
||||
|
||||
public function testEmptyCollectionComparisonExpression()
|
||||
{
|
||||
$this->assertSqlGeneration(
|
||||
"SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.phonenumbers IS EMPTY",
|
||||
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE (SELECT COUNT(c1_.user_id) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) = 0"
|
||||
);
|
||||
$this->assertSqlGeneration(
|
||||
"SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.phonenumbers IS NOT EMPTY",
|
||||
"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE (SELECT COUNT(c1_.user_id) FROM cms_phonenumbers c1_ WHERE c1_.user_id = c0_.id) > 0"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user