diff --git a/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php b/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php index cd6741fea..7ba814d53 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php @@ -45,45 +45,66 @@ class SizeFunction extends FunctionNode */ public function getSql(\Doctrine\ORM\Query\SqlWalker $sqlWalker) { + $platform = $sqlWalker->getConnection()->getDatabasePlatform(); $dqlAlias = $this->collectionPathExpression->identificationVariable; $assocField = $this->collectionPathExpression->field; $qComp = $sqlWalker->getQueryComponent($dqlAlias); - $assoc = $qComp['metadata']->associationMappings[$assocField]; - $sql = ''; - + $class = $qComp['metadata']; + $assoc = $class->associationMappings[$assocField]; + $sql = 'SELECT COUNT(*) FROM '; + if ($assoc->isOneToMany()) { $targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc->targetEntityName); - $targetAssoc = $targetClass->associationMappings[$assoc->mappedBy]; - $targetTableAlias = $sqlWalker->getSqlTableAlias($targetClass->table['name']); - $sourceTableAlias = $sqlWalker->getSqlTableAlias($qComp['metadata']->table['name'], $dqlAlias); + $sourceTableAlias = $sqlWalker->getSqlTableAlias($class->table['name'], $dqlAlias); + + $sql .= $targetClass->getQuotedTableName($platform) . ' ' . $targetTableAlias . ' WHERE '; + + $owningAssoc = $targetClass->associationMappings[$assoc->mappedBy]; + + $first = true; - $whereSql = ''; + foreach ($owningAssoc->targetToSourceKeyColumns as $targetColumn => $sourceColumn) { + if ($first) $first = false; else $sql .= ' AND '; - foreach ($targetAssoc->targetToSourceKeyColumns as $targetKeyColumn => $sourceKeyColumn) { - $whereSql .= (($whereSql == '') ? ' WHERE ' : ' AND ') - . $targetTableAlias . '.' . $sourceKeyColumn . ' = ' - . $sourceTableAlias . '.' . $targetKeyColumn; + $sql .= $targetTableAlias . '.' . $sourceColumn + . ' = ' + . $sourceTableAlias . '.' . $class->getQuotedColumnName($class->fieldNames[$targetColumn], $platform); } + } else { // many-to-many + $targetClass = $sqlWalker->getEntityManager()->getClassMetadata($assoc->targetEntityName); - $tableName = $targetClass->table['name']; - } else if ($assoc->isManyToMany()) { - $targetTableAlias = $sqlWalker->getSqlTableAlias($assoc->joinTable['name']); - $sourceTableAlias = $sqlWalker->getSqlTableAlias($qComp['metadata']->table['name'], $dqlAlias); - - $whereSql = ''; + $owningAssoc = $assoc->isOwningSide ? $assoc : $targetClass->associationMappings[$assoc->mappedBy]; + $joinTable = $owningAssoc->joinTable; - foreach ($assoc->relationToSourceKeyColumns as $targetKeyColumn => $sourceKeyColumn) { - $whereSql .= (($whereSql == '') ? ' WHERE ' : ' AND ') - . $targetTableAlias . '.' . $targetKeyColumn . ' = ' - . $sourceTableAlias . '.' . $sourceKeyColumn; + // SQL table aliases + $joinTableAlias = $sqlWalker->getSqlTableAlias($joinTable['name']); + $sourceTableAlias = $sqlWalker->getSqlTableAlias($class->table['name'], $dqlAlias); + + // join to target table + $sql .= $owningAssoc->getQuotedJoinTableName($platform) . ' ' . $joinTableAlias . ' WHERE '; + + $joinColumns = $assoc->isOwningSide + ? $joinTable['joinColumns'] + : $joinTable['inverseJoinColumns']; + + $first = true; + + foreach ($joinColumns as $joinColumn) { + if ($first) $first = false; else $sql .= ' AND '; + + $sourceColumnName = $class->getQuotedColumnName( + $class->fieldNames[$joinColumn['referencedColumnName']], $platform + ); + + $sql .= $joinTableAlias . '.' . $joinColumn['name'] + . ' = ' + . $sourceTableAlias . '.' . $sourceColumnName; } - - $tableName = $assoc->joinTable['name']; } - return '(SELECT COUNT(*) FROM ' . $tableName . ' ' . $targetTableAlias . $whereSql . ')'; + return '(' . $sql . ')'; } /** diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index b00ffcc9d..d30941884 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1393,7 +1393,7 @@ class SqlWalker implements TreeWalker $targetClass = $this->_em->getClassMetadata($assoc->targetEntityName); $owningAssoc = $assoc->isOwningSide ? $assoc : $targetClass->associationMappings[$assoc->mappedBy]; - $joinTable = $assoc->isOwningSide ? $assoc->joinTable : $owningAssoc->joinTable; + $joinTable = $owningAssoc->joinTable; // SQL table aliases $joinTableAlias = $this->getSqlTableAlias($joinTable['name']); @@ -1401,7 +1401,7 @@ class SqlWalker implements TreeWalker $sourceTableAlias = $this->getSqlTableAlias($class->table['name'], $dqlAlias); // join to target table - $sql .= $assoc->getQuotedJoinTableName($this->_platform) + $sql .= $owningAssoc->getQuotedJoinTableName($this->_platform) . ' ' . $joinTableAlias . ' INNER JOIN ' . $targetClass->getQuotedTableName($this->_platform) . ' ' . $targetTableAlias . ' ON '; diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC719Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC719Test.php new file mode 100644 index 000000000..5c6af8a42 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC719Test.php @@ -0,0 +1,107 @@ +_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger); + $this->_schemaTool->createSchema(array( + $this->_em->getClassMetadata(__NAMESPACE__ . '\DDC719Group'), + )); + } + + public function testIsEmptySqlGeneration() + { + $q = $this->_em->createQuery('SELECT g, c FROM Doctrine\Tests\ORM\Functional\Ticket\DDC719Group g LEFT JOIN g.children c WHERE g.parents IS EMPTY'); + + $this->assertEquals('SELECT g0_.name AS name0, g0_.description AS description1, g0_.id AS id2, g1_.name AS name3, g1_.description AS description4, g1_.id AS id5 FROM groups g0_ LEFT JOIN groups_groups g2_ ON g0_.id = g2_.parent_id LEFT JOIN groups g1_ ON g1_.id = g2_.child_id WHERE (SELECT COUNT(*) FROM groups_groups g3_ WHERE g3_.child_id = g0_.id) = 0', $q->getSQL()); + } +} + +/** + * @MappedSuperclass + */ +class Entity +{ + /** + * @Id @GeneratedValue + * @Column(type="integer") + */ + protected $id; + + public function getId() { return $this->id; } +} + +/** + * @Entity + * @Table(name="groups") + */ +class DDC719Group extends Entity { + /** @Column(type="string", nullable=false) */ + protected $name; + + /** @Column(type="string", nullable=true) */ + protected $description; + + /** + * @ManyToMany(targetEntity="DDC719Group", inversedBy="parents") + * @JoinTable(name="groups_groups", + * joinColumns={@JoinColumn(name="parent_id", referencedColumnName="id")}, + * inverseJoinColumns={@JoinColumn(name="child_id", referencedColumnName="id")} + * ) + */ + protected $children = NULL; + + /** + * @ManyToMany(targetEntity="DDC719Group", mappedBy="children") + */ + protected $parents = NULL; + + /** + * construct + */ + public function __construct() { + parent::__construct(); + + $this->channels = new ArrayCollection(); + $this->children = new ArrayCollection(); + $this->parents = new ArrayCollection(); + } + + /** + * adds group as new child + * + * @param Group $child + * @todo check against endless recursion + * @todo check if the group is already member of the group + */ + public function addGroup(Group $child) { + $this->children->add($child); + } + + /** + * adds channel as new child + * + * @param Channel $child + * @todo check if the channel is already member of the group + */ + public function addChannel(Channel $child) { + $this->channels->add($child); + } + + /** + * getter & setter + */ + public function getName() { return $this->name; } + public function setName($name) { $this->name = $name; } + public function getDescription() { return $this->description; } + public function setDescription($description) { $this->description = $description; } + public function getChildren() { return $this->children; } + public function getParents() { return $this->parents; } + public function getChannels() { return $this->channels; } +} \ No newline at end of file