diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 6875629f745..786384a1ac9 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -77,9 +77,8 @@ class SqlWalker implements TreeWalker /** * The DQL alias of the root class of the currently traversed query. - * TODO: May need to be turned into a stack for usage in subqueries */ - private $_currentRootAlias; + private $_rootAliases = array(); /** * Flag that indicates whether to generate SQL table aliases in the SQL. @@ -187,6 +186,7 @@ public function getExecutor($AST) /** * Generates a unique, short SQL table alias. * + * @param string $tableName Table name * @param string $dqlAlias The DQL alias. * @return string Generated table alias. */ @@ -308,14 +308,15 @@ private function _generateOrderedCollectionOrderByItems() /** * Generates a discriminator column SQL condition for the class with the given DQL alias. * - * @param string $dqlAlias + * @param array $dqlAliases List of root DQL aliases to inspect for discriminator restrictions. * @return string */ - private function _generateDiscriminatorColumnConditionSQL($dqlAlias) + private function _generateDiscriminatorColumnConditionSQL(array $dqlAliases) { + $encapsulate = false; $sql = ''; - if ($dqlAlias) { + foreach ($dqlAliases as $dqlAlias) { $class = $this->_queryComponents[$dqlAlias]['metadata']; if ($class->isInheritanceTypeSingleTable()) { @@ -329,14 +330,18 @@ private function _generateDiscriminatorColumnConditionSQL($dqlAlias) $values[] = $conn->quote($this->_em->getClassMetadata($subclassName)->discriminatorValue); } - $sql .= (($this->_useSqlTableAliases) - ? $this->getSqlTableAlias($class->table['name'], $dqlAlias) . '.' : '' - ) . $class->discriminatorColumn['name'] - . ' IN (' . implode(', ', $values) . ')'; + if ($sql != '') { + $sql .= ' AND '; + $encapsulate = true; + } + + $sql .= ($sql != '' ? ' AND ' : '') + . (($this->_useSqlTableAliases) ? $this->getSqlTableAlias($class->table['name'], $dqlAlias) . '.' : '') + . $class->discriminatorColumn['name'] . ' IN (' . implode(', ', $values) . ')'; } } - return $sql; + return ($encapsulate) ? '(' . $sql . ')' : $sql; } /** @@ -351,7 +356,7 @@ public function walkSelectStatement(AST\SelectStatement $AST) if (($whereClause = $AST->whereClause) !== null) { $sql .= $this->walkWhereClause($whereClause); - } else if (($discSql = $this->_generateDiscriminatorColumnConditionSQL($this->_currentRootAlias)) !== '') { + } else if (($discSql = $this->_generateDiscriminatorColumnConditionSQL($this->_rootAliases)) !== '') { $sql .= ' WHERE ' . $discSql; } @@ -399,7 +404,7 @@ public function walkUpdateStatement(AST\UpdateStatement $AST) if (($whereClause = $AST->whereClause) !== null) { $sql .= $this->walkWhereClause($whereClause); - } else if (($discSql = $this->_generateDiscriminatorColumnConditionSQL($this->_currentRootAlias)) !== '') { + } else if (($discSql = $this->_generateDiscriminatorColumnConditionSQL($this->_rootAliases)) !== '') { $sql .= ' WHERE ' . $discSql; } @@ -419,7 +424,7 @@ public function walkDeleteStatement(AST\DeleteStatement $AST) if (($whereClause = $AST->whereClause) !== null) { $sql .= $this->walkWhereClause($whereClause); - } else if (($discSql = $this->_generateDiscriminatorColumnConditionSQL($this->_currentRootAlias)) !== '') { + } else if (($discSql = $this->_generateDiscriminatorColumnConditionSQL($this->_rootAliases)) !== '') { $sql .= ' WHERE ' . $discSql; } @@ -599,34 +604,40 @@ public function walkSelectClause($selectClause) */ public function walkFromClause($fromClause) { - $sql = ' FROM '; $identificationVarDecls = $fromClause->identificationVariableDeclarations; - $firstIdentificationVarDecl = $identificationVarDecls[0]; - $rangeDecl = $firstIdentificationVarDecl->rangeVariableDeclaration; - $dqlAlias = $rangeDecl->aliasIdentificationVariable; + $sqlParts = array(); - $this->_currentRootAlias = $dqlAlias; + foreach ($identificationVarDecls as $identificationVariableDecl) { + $sql = ''; - $class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName); - $sql .= $class->getQuotedTableName($this->_platform) . ' ' - . $this->getSqlTableAlias($class->table['name'], $dqlAlias); + $rangeDecl = $identificationVariableDecl->rangeVariableDeclaration; + $dqlAlias = $rangeDecl->aliasIdentificationVariable; + + $this->_rootAliases[] = $dqlAlias; - if ($class->isInheritanceTypeJoined()) { - $sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias); - } + $class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName); + $sql .= $class->getQuotedTableName($this->_platform) . ' ' + . $this->getSqlTableAlias($class->table['name'], $dqlAlias); - foreach ($firstIdentificationVarDecl->joinVariableDeclarations as $joinVarDecl) { - $sql .= $this->walkJoinVariableDeclaration($joinVarDecl); - } + if ($class->isInheritanceTypeJoined()) { + $sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias); + } - if ($firstIdentificationVarDecl->indexBy) { - $this->_rsm->addIndexBy( - $firstIdentificationVarDecl->indexBy->simpleStateFieldPathExpression->identificationVariable, - $firstIdentificationVarDecl->indexBy->simpleStateFieldPathExpression->parts[0] - ); + foreach ($identificationVariableDecl->joinVariableDeclarations as $joinVarDecl) { + $sql .= $this->walkJoinVariableDeclaration($joinVarDecl); + } + + if ($identificationVariableDecl->indexBy) { + $this->_rsm->addIndexBy( + $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->identificationVariable, + $identificationVariableDecl->indexBy->simpleStateFieldPathExpression->parts[0] + ); + } + + $sqlParts[] = $this->_platform->appendLockHint($sql, $this->_query->getHint(Query::HINT_LOCK_MODE)); } - return $this->_platform->appendLockHint($sql, $this->_query->getHint(Query::HINT_LOCK_MODE)); + return ' FROM ' . implode(', ', $sqlParts); } /** @@ -802,7 +813,7 @@ public function walkJoinVariableDeclaration($joinVarDecl) $sql .= ' AND (' . $this->walkConditionalExpression($condExpr) . ')'; } - $discrSql = $this->_generateDiscriminatorColumnConditionSQL($joinedDqlAlias); + $discrSql = $this->_generateDiscriminatorColumnConditionSQL(array($joinedDqlAlias)); if ($discrSql) { $sql .= ' AND ' . $discrSql; @@ -1045,23 +1056,30 @@ public function walkSubselect($subselect) public function walkSubselectFromClause($subselectFromClause) { $identificationVarDecls = $subselectFromClause->identificationVariableDeclarations; - $firstIdentificationVarDecl = $identificationVarDecls[0]; - $rangeDecl = $firstIdentificationVarDecl->rangeVariableDeclaration; - $dqlAlias = $rangeDecl->aliasIdentificationVariable; + $sqlParts = array (); - $class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName); - $sql = ' FROM ' . $class->getQuotedTableName($this->_platform) . ' ' - . $this->getSqlTableAlias($class->table['name'], $dqlAlias); + foreach ($identificationVarDecls as $subselectIdVarDecl) { + $sql = ''; - if ($class->isInheritanceTypeJoined()) { - $sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias); - } + $rangeDecl = $subselectIdVarDecl->rangeVariableDeclaration; + $dqlAlias = $rangeDecl->aliasIdentificationVariable; + + $class = $this->_em->getClassMetadata($rangeDecl->abstractSchemaName); + $sql .= $class->getQuotedTableName($this->_platform) . ' ' + . $this->getSqlTableAlias($class->table['name'], $dqlAlias); - foreach ($firstIdentificationVarDecl->joinVariableDeclarations as $joinVarDecl) { - $sql .= $this->walkJoinVariableDeclaration($joinVarDecl); + if ($class->isInheritanceTypeJoined()) { + $sql .= $this->_generateClassTableInheritanceJoins($class, $dqlAlias); + } + + foreach ($subselectIdVarDecl->joinVariableDeclarations as $joinVarDecl) { + $sql .= $this->walkJoinVariableDeclaration($joinVarDecl); + } + + $sqlParts[] = $this->_platform->appendLockHint($sql, $this->_query->getHint(Query::HINT_LOCK_MODE)); } - return $sql; + return ' FROM ' . implode(', ', $sqlParts); } /** @@ -1161,7 +1179,7 @@ public function walkDeleteClause(AST\DeleteClause $deleteClause) $sql .= ' ' . $this->getSqlTableAlias($class->getTableName()); } - $this->_currentRootAlias = $deleteClause->aliasIdentificationVariable; + $this->_rootAliases[] = $deleteClause->aliasIdentificationVariable; return $sql; } @@ -1182,7 +1200,7 @@ public function walkUpdateClause($updateClause) $sql .= ' ' . $this->getSqlTableAlias($class->getTableName()); } - $this->_currentRootAlias = $updateClause->aliasIdentificationVariable; + $this->_rootAliases[] = $updateClause->aliasIdentificationVariable; $sql .= ' SET ' . implode( ', ', array_map(array($this, 'walkUpdateItem'), $updateClause->updateItems) @@ -1227,7 +1245,7 @@ public function walkUpdateItem($updateItem) */ public function walkWhereClause($whereClause) { - $discrSql = $this->_generateDiscriminatorColumnConditionSql($this->_currentRootAlias); + $discrSql = $this->_generateDiscriminatorColumnConditionSql($this->_rootAliases); $condSql = $this->walkConditionalExpression($whereClause->conditionalExpression); return ' WHERE ' . (( ! $discrSql) ? $condSql : '(' . $condSql . ') AND ' . $discrSql); diff --git a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php index 039cdb97559..23640142f4a 100644 --- a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php +++ b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php @@ -79,6 +79,11 @@ public function testSelectSingleComponentWithMultipleColumns() $this->assertValidDql('SELECT u.name, u.username FROM Doctrine\Tests\Models\CMS\CmsUser u'); } + public function testSelectMultipleComponentsUsingMultipleFrom() + { + $this->assertValidDql('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE u = p.user'); + } + public function testSelectMultipleComponentsWithAsterisk() { $this->assertValidDql('SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.phonenumbers p'); diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 0f20ac279c3..21cea7f08ad 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -72,6 +72,14 @@ public function testSupportsSelectForMultipleColumnsOfASingleComponent() ); } + public function testSupportsSelectUsingMultipleFromComponents() + { + $this->assertSqlGeneration( + 'SELECT u, p FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE u = p.user', + 'SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, c1_.phonenumber AS phonenumber4 FROM cms_users c0_, cms_phonenumbers c1_ WHERE c0_.id = c1_.user_id' + ); + } + public function testSupportsSelectWithCollectionAssociationJoin() { $this->assertSqlGeneration( @@ -187,8 +195,16 @@ public function testSupportsArithmeticExpressionsInWherePart() public function testSupportsMultipleEntitiesInFromClause() { $this->assertSqlGeneration( - 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsArticle a WHERE u.id = a.user.id', - 'SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, c1_.id AS id4, c1_.topic AS topic5, c1_.text AS text6, c1_.version AS version7 FROM cms_users c0_ INNER JOIN cms_users c2_ ON c1_.user_id = c2_.id WHERE c0_.id = c2_.id' + 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsArticle a JOIN a.user u2 WHERE u.id = u2.id', + 'SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, c1_.id AS id4, c1_.topic AS topic5, c1_.text AS text6, c1_.version AS version7 FROM cms_users c0_, cms_articles c1_ INNER JOIN cms_users c2_ ON c1_.user_id = c2_.id WHERE c0_.id = c2_.id' + ); + } + + public function testSupportsMultipleEntitiesInFromClauseUsingPathExpression() + { + $this->assertSqlGeneration( + 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsArticle a WHERE u.id = a.user', + 'SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3, c1_.id AS id4, c1_.topic AS topic5, c1_.text AS text6, c1_.version AS version7 FROM cms_users c0_, cms_articles c1_ WHERE c0_.id = c1_.user_id' ); }