1
0
mirror of synced 2024-12-12 22:36:02 +03:00

[2.0] Implemented SQL generation for SIZE() function and EmptyCollectionComparisonExpression.

This commit is contained in:
romanb 2009-08-05 15:47:41 +00:00
parent fde6431d1d
commit f64347d899
6 changed files with 99 additions and 21 deletions

View File

@ -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'.");
}

View File

@ -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;
}
/**

View File

@ -1970,7 +1970,7 @@ class Parser
return $this->AggregateExpression();
}
return $this->FunctionsReturningStrings();
return $this->FunctionDeclaration();
}
$this->syntaxError();

View File

@ -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.");

View File

@ -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
*

View File

@ -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"
);
}
}