diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadata.php b/lib/Doctrine/ORM/Mapping/ClassMetadata.php index 725343c8f..8e58ad5ef 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadata.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadata.php @@ -159,7 +159,7 @@ class ClassMetadata extends ClassMetadataInfo * with the same order as the field order in {@link identifier}. * * @param object $entity - * @return mixed + * @return array */ public function getIdentifierValues($entity) { diff --git a/lib/Doctrine/ORM/Query.php b/lib/Doctrine/ORM/Query.php index 80f13bc33..f51b1fadc 100644 --- a/lib/Doctrine/ORM/Query.php +++ b/lib/Doctrine/ORM/Query.php @@ -220,8 +220,9 @@ final class Query extends AbstractQuery if (is_object($value)) { $values = $this->_em->getClassMetadata(get_class($value))->getIdentifierValues($value); + //var_dump($this->_em->getUnitOfWork()->getEntityIdentifier($value)); $sqlPositions = $paramMappings[$key]; - $sqlParams = array_merge($sqlParams, array_combine((array)$sqlPositions, (array)$values)); + $sqlParams = array_merge($sqlParams, array_combine((array)$sqlPositions, $values)); } else if (is_bool($value)) { $boolValue = $this->_em->getConnection()->getDatabasePlatform()->convertBooleans($value); foreach ($paramMappings[$key] as $position) { diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index cc4faffc1..a8767e539 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -417,10 +417,10 @@ class Parser */ private function _isFunction() { - $peek = $this->_lexer->peek(); + $peek = $this->_lexer->peek(); $nextpeek = $this->_lexer->peek(); $this->_lexer->resetPeek(); - + // We deny the COUNT(SELECT * FROM User u) here. COUNT won't be considered a function return ($peek['value'] === '(' && $nextpeek['type'] !== Lexer::T_SELECT); } @@ -929,10 +929,9 @@ class Parser $identVariable = $this->IdentificationVariable(); $this->match(Lexer::T_DOT); - //TODO: $this->match($this->_lexer->lookahead['value']); - $this->match(Lexer::T_IDENTIFIER); + $this->match($this->_lexer->lookahead['type']); $field = $this->_lexer->token['value']; - + // Validate association field $qComp = $this->_queryComponents[$identVariable]; $class = $qComp['metadata']; @@ -1878,17 +1877,7 @@ class Parser if ($this->_lexer->isNextToken(Lexer::T_OPEN_PARENTHESIS)) { // Peek beyond the matching closing paranthesis ')' - $numUnmatched = 1; - $peek = $this->_lexer->peek(); - while ($numUnmatched > 0 && $peek !== null) { - if ($peek['value'] == ')') { - --$numUnmatched; - } else if ($peek['value'] == '(') { - ++$numUnmatched; - } - $peek = $this->_lexer->peek(); - } - $this->_lexer->resetPeek(); + $peek = $this->_peekBeyondClosingParenthesis(); if (in_array($peek['value'], array("=", "<", "<=", "<>", ">", ">=", "!=")) || $peek['type'] === Lexer::T_NOT || @@ -1928,70 +1917,75 @@ class Parser return $this->ExistsExpression(); } - $pathExprOrInputParam = false; - + $peek = $this->_lexer->glimpse(); + if ($token['type'] === Lexer::T_IDENTIFIER || $token['type'] === Lexer::T_INPUT_PARAMETER) { - // Peek beyond the PathExpression - $pathExprOrInputParam = true; - $peek = $this->_lexer->peek(); - - while ($peek['value'] === '.') { + if ($peek['value'] == '(') { + // Peek beyond the matching closing paranthesis ')' $this->_lexer->peek(); + $token = $this->_peekBeyondClosingParenthesis(); + } else { + // Peek beyond the PathExpression (or InputParameter) $peek = $this->_lexer->peek(); - } - // Also peek beyond a NOT if there is one - if ($peek['type'] === Lexer::T_NOT) { - $peek = $this->_lexer->peek(); - } - - $token = $peek; - - // We need to go even further in case of IS (differenciate between NULL and EMPTY) - $lookahead = $this->_lexer->peek(); - - // Also peek beyond a NOT if there is one - if ($lookahead['type'] === Lexer::T_NOT) { + while ($peek['value'] === '.') { + $this->_lexer->peek(); + $peek = $this->_lexer->peek(); + } + + // Also peek beyond a NOT if there is one + if ($peek['type'] === Lexer::T_NOT) { + $peek = $this->_lexer->peek(); + } + + $token = $peek; + + // We need to go even further in case of IS (differenciate between NULL and EMPTY) $lookahead = $this->_lexer->peek(); - } - - $this->_lexer->resetPeek(); - } - if ($pathExprOrInputParam) { - switch ($token['type']) { - case Lexer::T_EQUALS: - case Lexer::T_LOWER_THAN: - case Lexer::T_GREATER_THAN: - case Lexer::T_NEGATE: - case Lexer::T_OPEN_PARENTHESIS: - return $this->ComparisonExpression(); + // Also peek beyond a NOT if there is one + if ($lookahead['type'] === Lexer::T_NOT) { + $lookahead = $this->_lexer->peek(); + } - case Lexer::T_BETWEEN: - return $this->BetweenExpression(); - - case Lexer::T_LIKE: - return $this->LikeExpression(); - - case Lexer::T_IN: - return $this->InExpression(); - - case Lexer::T_IS: - if ($lookahead['type'] == Lexer::T_NULL) { - return $this->NullComparisonExpression(); - } - - return $this->EmptyCollectionComparisonExpression(); - - case Lexer::T_MEMBER: - return $this->CollectionMemberExpression(); - - default: - $this->syntaxError(); + $this->_lexer->resetPeek(); } } + + switch ($token['type']) { + case Lexer::T_BETWEEN: + return $this->BetweenExpression(); + case Lexer::T_LIKE: + return $this->LikeExpression(); + case Lexer::T_IN: + return $this->InExpression(); + case Lexer::T_IS: + if ($lookahead['type'] == Lexer::T_NULL) { + return $this->NullComparisonExpression(); + } + return $this->EmptyCollectionComparisonExpression(); + case Lexer::T_MEMBER: + return $this->CollectionMemberExpression(); + default: + return $this->ComparisonExpression(); + } + } + + private function _peekBeyondClosingParenthesis() + { + $numUnmatched = 1; + $token = $this->_lexer->peek(); + while ($numUnmatched > 0 && $token !== null) { + if ($token['value'] == ')') { + --$numUnmatched; + } else if ($token['value'] == '(') { + ++$numUnmatched; + } + $token = $this->_lexer->peek(); + } + $this->_lexer->resetPeek(); - return $this->ComparisonExpression(); + return $token; } /** diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 86f6e4dc4..19120afd1 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -442,7 +442,7 @@ class SqlWalker implements TreeWalker throw QueryException::associationPathCompositeKeyNotSupported(); } $sql .= $this->walkIdentificationVariable($dqlAlias) . '.' - . $assoc->getQuotedJoinColumnName(key($assoc->sourceToTargetKeyColumns), $this->_platform); + . $assoc->getQuotedJoinColumnName(reset($assoc->targetToSourceKeyColumns), $this->_platform); } else { // 2- Inverse side: NOT (YET?) SUPPORTED throw QueryException::associationPathInverseSideNotSupported(); @@ -466,7 +466,7 @@ class SqlWalker implements TreeWalker $sql = 'SELECT ' . (($selectClause->isDistinct) ? 'DISTINCT ' : '') . implode( ', ', array_map(array($this, 'walkSelectExpression'), $selectClause->selectExpressions) ); - + $addMetaColumns = ! $this->_query->getHint(Query::HINT_FORCE_PARTIAL_LOAD) && $this->_query->getHydrationMode() == Query::HYDRATE_OBJECT || @@ -521,7 +521,7 @@ class SqlWalker implements TreeWalker } } } else { - // Add foreign key columns to SQL, if necessary + // Add foreign key columns to SQL, if necessary if ($addMetaColumns) { $sqlTableAlias = $this->getSqlTableAlias($class->primaryTable['name'], $dqlAlias); foreach ($class->associationMappings as $assoc) { diff --git a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php index 6a0c6d937..0b7c5ac69 100644 --- a/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/BasicFunctionalTest.php @@ -547,7 +547,6 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase public function testFlushDoesNotIssueUnnecessaryUpdates() { - $user = new CmsUser; $user->name = 'Guilherme'; $user->username = 'gblanco'; @@ -613,6 +612,35 @@ class BasicFunctionalTest extends \Doctrine\Tests\OrmFunctionalTestCase //$this->_em->getConnection()->getConfiguration()->setSqlLogger(null); } + /** + * @group ref + */ + /*public function testQueryEntityByReference() + { + $user = new CmsUser; + $user->name = 'Guilherme'; + $user->username = 'gblanco'; + $user->status = 'developer'; + + $address = new CmsAddress; + $address->country = 'Germany'; + $address->city = 'Berlin'; + $address->zip = '12345'; + + $user->setAddress($address); + + $this->_em->persist($user); + $this->_em->flush(); + $this->_em->clear(); + + $userRef = $this->_em->getReference('Doctrine\Tests\Models\CMS\CmsUser', $user->getId()); + $address2 = $this->_em->createQuery('select a from Doctrine\Tests\Models\CMS\CmsAddress a where a.user = :user') + ->setParameter('user', $userRef) + ->getSingleResult(); + + + }*/ + //DRAFT OF EXPECTED/DESIRED BEHAVIOR /*public function testPersistentCollectionContainsDoesNeverInitialize() { diff --git a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php index 59a1ca65f..8183891e0 100644 --- a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php +++ b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php @@ -346,10 +346,11 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase { $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id > SOME (SELECT u2.id FROM Doctrine\Tests\Models\CMS\CmsUser u2 WHERE u2.name = u.name)'); } - + public function testMemberOfExpression() { $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :param MEMBER OF u.phonenumbers'); + //$this->assertValidDql("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE 'Joe' MEMBER OF u.nicknames"); } public function testSizeFunction() @@ -407,6 +408,11 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase { $this->assertValidDql('SELECT u, u.id + ?1 AS someNumber FROM Doctrine\Tests\Models\CMS\CmsUser u'); } + + public function testDQLKeywordInJoinIsAllowed() + { + $this->assertValidDql('SELECT u FROM ' . __NAMESPACE__ . '\DQLKeywordsModelUser u JOIN u.group g'); + } /* The exception is currently thrown in the SQLWalker, not earlier. public function testInverseSideSingleValuedAssociationPathNotAllowed() @@ -414,4 +420,20 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase $this->assertInvalidDql('SELECT u.id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.address = ?1'); } */ -} \ No newline at end of file +} + +/** @Entity */ +class DQLKeywordsModelUser +{ + /** @Id @Column(type="integer") @GeneratedValue */ + private $id; + /** @OneToOne(targetEntity="DQLKeywordsModelGroup") */ + private $group; +} + +/** @Entity */ +class DQLKeywordsModelGroup +{ + /** @Id @Column(type="integer") @GeneratedValue */ + private $id; +} diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index e69e7bffa..74c92ff52 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -483,7 +483,6 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } - public function testBooleanLiteralInWhereOnSqlite() { $oldPlat = $this->_em->getConnection()->getDatabasePlatform(); @@ -519,22 +518,14 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase $this->_em->getConnection()->setDatabasePlatform($oldPlat); } - - - /* Not yet implemented, needs more thought */ + public function testSingleValuedAssociationFieldInWhere() { - /*$this->assertSqlGeneration( - "SELECT p FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.user = ?1", - "SELECT c0_.id AS id0, c0_user_id AS user_id1, c0_.phonenumber AS phonenumber2 FROM cms_phonenumbers c0_ WHERE c0_.user_id = ?" - ); $this->assertSqlGeneration( - "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.address = ?1", - //"SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE c0_.id = (SELECT c1_.user_id FROM cms_addresses c1_ WHERE c1_.id = ?)" - "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ WHERE EXISTS (SELECT 1 FROM cms_addresses c1_ WHERE c1_.user_id = c0_.id AND c1_.id = ?)" - );*/ + "SELECT p FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.user = ?1", + "SELECT c0_.phonenumber AS phonenumber0 FROM cms_phonenumbers c0_ WHERE c0_.user_id = ?" + ); } - public function testSingleValuedAssociationNullCheckOnOwningSide() { @@ -554,4 +545,23 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ LEFT JOIN cms_addresses c1_ ON c0_.id = c1_.user_id WHERE c1_.id IS NULL" ); } + + /** + * @group DDC-339 + */ + public function testStringFunctionLikeExpression() + { + $this->assertSqlGeneration( + "SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE LOWER(u.name) LIKE '%foo OR bar%'", + "SELECT c0_.name AS name0 FROM cms_users c0_ WHERE LOWER(c0_.name) LIKE '%foo OR bar%'" + ); + $this->assertSqlGeneration( + "SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE LOWER(u.name) LIKE :str", + "SELECT c0_.name AS name0 FROM cms_users c0_ WHERE LOWER(c0_.name) LIKE ?" + ); + $this->assertSqlGeneration( + "SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE CONCAT(UPPER(u.name), '_moo') LIKE :str", + "SELECT c0_.name AS name0 FROM cms_users c0_ WHERE UPPER(c0_.name) || '_moo' LIKE ?" + ); + } }