From 84b9eda17c8faead9ea59b39f12d1b78aa5ba823 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Wed, 11 Aug 2010 23:12:44 -0300 Subject: [PATCH 1/4] [DDC-680] Implemented support to complex mathematical expression in subselect part aswell as improved support of comparison expressions. --- lib/Doctrine/ORM/Query/Lexer.php | 88 ++++++------ lib/Doctrine/ORM/Query/Parser.php | 128 +++++++++++++----- lib/Doctrine/ORM/Query/SqlWalker.php | 52 ++++++- .../ORM/Query/LanguageRecognitionTest.php | 24 +++- .../ORM/Query/SelectSqlGenerationTest.php | 27 ++++ 5 files changed, 235 insertions(+), 84 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Lexer.php b/lib/Doctrine/ORM/Query/Lexer.php index 57ffc3ebe..673ab0205 100644 --- a/lib/Doctrine/ORM/Query/Lexer.php +++ b/lib/Doctrine/ORM/Query/Lexer.php @@ -61,48 +61,52 @@ class Lexer extends \Doctrine\Common\Lexer const T_BETWEEN = 107; const T_BOTH = 108; const T_BY = 109; - const T_COUNT = 110; - const T_DELETE = 111; - const T_DESC = 112; - const T_DISTINCT = 113; - const T_EMPTY = 114; - const T_ESCAPE = 115; - const T_EXISTS = 116; - const T_FALSE = 117; - const T_FROM = 118; - const T_GROUP = 119; - const T_HAVING = 120; - const T_IN = 121; - const T_INDEX = 122; - const T_INNER = 123; - const T_INSTANCE = 124; - const T_IS = 125; - const T_JOIN = 126; - const T_LEADING = 127; - const T_LEFT = 128; - const T_LIKE = 129; - const T_MAX = 130; - const T_MEMBER = 131; - const T_MIN = 132; - const T_NOT = 133; - const T_NULL = 134; - const T_OF = 135; - const T_OR = 136; - const T_ORDER = 137; - const T_OUTER = 138; - const T_SELECT = 139; - const T_SET = 140; - const T_SIZE = 141; - const T_SOME = 142; - const T_SUM = 143; - const T_TRAILING = 144; - const T_TRUE = 145; - const T_UPDATE = 146; - const T_WHERE = 147; - const T_WITH = 148; - const T_PARTIAL = 149; - const T_MOD = 150; - + const T_CASE = 110; + const T_COALESCE = 111; + const T_COUNT = 112; + const T_DELETE = 113; + const T_DESC = 114; + const T_DISTINCT = 115; + const T_EMPTY = 116; + const T_ESCAPE = 117; + const T_EXISTS = 118; + const T_FALSE = 119; + const T_FROM = 120; + const T_GROUP = 121; + const T_HAVING = 122; + const T_IN = 123; + const T_INDEX = 124; + const T_INNER = 125; + const T_INSTANCE = 126; + const T_IS = 127; + const T_JOIN = 128; + const T_LEADING = 129; + const T_LEFT = 130; + const T_LIKE = 131; + const T_MAX = 132; + const T_MEMBER = 133; + const T_MIN = 134; + const T_NOT = 135; + const T_NULL = 136; + const T_NULLIF = 137; + const T_OF = 138; + const T_OR = 139; + const T_ORDER = 140; + const T_OUTER = 141; + const T_SELECT = 142; + const T_SET = 143; + const T_SIZE = 144; + const T_SOME = 145; + const T_SUM = 146; + const T_TRAILING = 147; + const T_TRUE = 148; + const T_UPDATE = 149; + const T_WHEN = 150; + const T_WHERE = 151; + const T_WITH = 153; + const T_PARTIAL = 154; + const T_MOD = 155; + /** * Creates a new query scanner object. * diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 2a2fa70cd..5015394f7 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -397,6 +397,45 @@ class Parser return $peek; } + /** + * Peek beyond the matched closing parenthesis and return the first token after that one. + * + * @return array + */ + private function _peekBeyondClosingParenthesis() + { + $token = $this->_lexer->peek(); + $numUnmatched = 1; + + while ($numUnmatched > 0 && $token !== null) { + if ($token['value'] == ')') { + --$numUnmatched; + } else if ($token['value'] == '(') { + ++$numUnmatched; + } + + $token = $this->_lexer->peek(); + } + + $this->_lexer->resetPeek(); + + return $token; + } + + /** + * Checks if the given token indicates a mathematical operator. + * + * @return boolean TRUE is the token is a mathematical operator, FALSE otherwise. + */ + private function _isMathOperator($token) + { + if (in_array($token['value'], array("+", "-", "/", "*"))) { + return true; + } + + return false; + } + /** * Checks if the next-next (after lookahead) token starts a function. * @@ -451,7 +490,7 @@ class Parser } /** - * Validates that the given IdentificationVariable is a semantically correct. + * Validates that the given IdentificationVariable is semantically correct. * It must exist in query components list. * * @return void @@ -486,6 +525,12 @@ class Parser } } + /** + * Validates that the given PartialObjectExpression is semantically correct. + * It must exist in query components list. + * + * @return void + */ private function _processDeferredPartialObjectExpressions() { foreach ($this->_deferredPartialObjectExpressions as $deferredItem) { @@ -511,7 +556,7 @@ class Parser } /** - * Validates that the given ResultVariable is a semantically correct. + * Validates that the given ResultVariable is semantically correct. * It must exist in query components list. * * @return void @@ -547,7 +592,7 @@ class Parser } /** - * Validates that the given PathExpression is a semantically correct for grammar rules: + * Validates that the given PathExpression is semantically correct for grammar rules: * * AssociationPathExpression ::= CollectionValuedPathExpression | SingleValuedAssociationPathExpression * SingleValuedPathExpression ::= StateFieldPathExpression | SingleValuedAssociationPathExpression @@ -1537,7 +1582,7 @@ class Parser $peek = $this->_lexer->peek(); // lookahead => token after the token after the '.' $this->_lexer->resetPeek(); - if ($peek['value'] == '+' || $peek['value'] == '-' || $peek['value'] == '/' || $peek['value'] == '*') { + if ($this->_isMathOperator($peek)) { return $this->SimpleArithmeticExpression(); } @@ -1545,6 +1590,14 @@ class Parser } else if ($lookahead == Lexer::T_INTEGER || $lookahead == Lexer::T_FLOAT) { return $this->SimpleArithmeticExpression(); } else if ($this->_isFunction()) { + // We may be in an ArithmeticExpression (find the matching ")" and inspect for Math operator) + $this->_lexer->peek(); // "(" + $peek = $this->_peekBeyondClosingParenthesis(); + + if ($this->_isMathOperator($peek)) { + return $this->SimpleArithmeticExpression(); + } + return $this->FunctionDeclaration(); } else if ($lookahead == Lexer::T_STRING) { return $this->StringPrimary(); @@ -1603,7 +1656,12 @@ class Parser $expression = $this->SimpleArithmeticExpression(); } } else if ($this->_isFunction()) { - if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { + $this->_lexer->peek(); // "(" + $beyond = $this->_peekBeyondClosingParenthesis(); + + if ($this->_isMathOperator($beyond)) { + $expression = $this->ScalarExpression(); + } else if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { $expression = $this->AggregateExpression(); } else { // Shortcut: ScalarExpression => Function @@ -1644,26 +1702,51 @@ class Parser } /** - * SimpleSelectExpression ::= StateFieldPathExpression | IdentificationVariable | (AggregateExpression [["AS"] AliasResultVariable]) + * SimpleSelectExpression ::= + * StateFieldPathExpression | IdentificationVariable | + * ((AggregateExpression | "(" Subselect ")" | ScalarExpression) [["AS"] AliasResultVariable]) * * @return \Doctrine\ORM\Query\AST\SimpleSelectExpression */ public function SimpleSelectExpression() { - if ($this->_lexer->isNextToken(Lexer::T_IDENTIFIER)) { - // SingleValuedPathExpression | IdentificationVariable - $glimpse = $this->_lexer->glimpse(); + $peek = $this->_lexer->glimpse(); - if ($glimpse['type'] == Lexer::T_DOT) { - return new AST\SimpleSelectExpression($this->StateFieldPathExpression()); + if ($peek['value'] != '(' && $this->_lexer->lookahead['type'] === Lexer::T_IDENTIFIER) { + // SingleValuedPathExpression | IdentificationVariable + if ($peek['value'] == '.') { + $expression = $this->StateFieldPathExpression(); + } else { + $expression = $this->IdentificationVariable(); } - $this->match(Lexer::T_IDENTIFIER); + return new AST\SimpleSelectExpression($expression); + } else if ($this->_lexer->lookahead['value'] == '(') { + if ($peek['type'] == Lexer::T_SELECT) { + // Subselect + $this->match(Lexer::T_OPEN_PARENTHESIS); + $expression = $this->Subselect(); + $this->match(Lexer::T_CLOSE_PARENTHESIS); + } else { + // Shortcut: ScalarExpression => SimpleArithmeticExpression + $expression = $this->SimpleArithmeticExpression(); + } - return new AST\SimpleSelectExpression($this->_lexer->token['value']); + return new AST\SimpleSelectExpression($expression); } - $expr = new AST\SimpleSelectExpression($this->AggregateExpression()); + $this->_lexer->peek(); + $beyond = $this->_peekBeyondClosingParenthesis(); + + if ($this->_isMathOperator($beyond)) { + $expression = $this->ScalarExpression(); + } else if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { + $expression = $this->AggregateExpression(); + } else { + $expression = $this->FunctionDeclaration(); + } + + $expr = new AST\SimpleSelectExpression($expression); if ($this->_lexer->isNextToken(Lexer::T_AS)) { $this->match(Lexer::T_AS); @@ -1869,23 +1952,6 @@ class Parser } } - 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 $token; - } - /** * EmptyCollectionComparisonExpression ::= CollectionValuedPathExpression "IS" ["NOT"] "EMPTY" * diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index baaafcf77..e43965ee7 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1106,7 +1106,7 @@ class SqlWalker implements TreeWalker $expr = $simpleSelectExpression->expression; if ($expr instanceof AST\PathExpression) { - $sql .= ' ' . $this->walkPathExpression($expr); + $sql .= $this->walkPathExpression($expr); } else if ($expr instanceof AST\AggregateExpression) { if ( ! $simpleSelectExpression->fieldIdentificationVariable) { $alias = $this->_scalarResultCounter++; @@ -1114,17 +1114,55 @@ class SqlWalker implements TreeWalker $alias = $simpleSelectExpression->fieldIdentificationVariable; } - $sql .= ' ' . $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias; + $sql .= $this->walkAggregateExpression($expr) . ' AS dctrn__' . $alias; + } else if ($expr instanceof AST\Subselect) { + if ( ! $simpleSelectExpression->fieldIdentificationVariable) { + $alias = $this->_scalarResultCounter++; + } else { + $alias = $simpleSelectExpression->fieldIdentificationVariable; + } + + $columnAlias = 'sclr' . $this->_aliasCounter++; + $sql .= '(' . $this->walkSubselect($expr) . ') AS ' . $columnAlias; + $this->_scalarResultAliasMap[$alias] = $columnAlias; + } else if ($expr instanceof AST\Functions\FunctionNode) { + if ( ! $simpleSelectExpression->fieldIdentificationVariable) { + $alias = $this->_scalarResultCounter++; + } else { + $alias = $simpleSelectExpression->fieldIdentificationVariable; + } + + $columnAlias = 'sclr' . $this->_aliasCounter++; + $sql .= $this->walkFunction($expr) . ' AS ' . $columnAlias; + $this->_scalarResultAliasMap[$alias] = $columnAlias; + } else if ( + $expr instanceof AST\SimpleArithmeticExpression || + $expr instanceof AST\ArithmeticTerm || + $expr instanceof AST\ArithmeticFactor || + $expr instanceof AST\ArithmeticPrimary + ) { + if ( ! $simpleSelectExpression->fieldIdentificationVariable) { + $alias = $this->_scalarResultCounter++; + } else { + $alias = $simpleSelectExpression->fieldIdentificationVariable; + } + + $columnAlias = 'sclr' . $this->_aliasCounter++; + $sql .= $this->walkSimpleArithmeticExpression($expr) . ' AS ' . $columnAlias; + $this->_scalarResultAliasMap[$alias] = $columnAlias; } else { // IdentificationVariable - // FIXME: Composite key support, or select all columns? Does that make sense - // in a subquery? $class = $this->_queryComponents[$expr]['metadata']; - $sql .= ' ' . $this->getSqlTableAlias($class->getTableName(), $expr) . '.' - . $class->getQuotedColumnName($class->identifier[0], $this->_platform); + $tableAlias = $this->getSqlTableAlias($class->getTableName(), $expr); + $first = true; + + foreach ($class->identifier as $identifier) { + if ($first) $first = false; else $sql .= ', '; + $sql .= $tableAlias . '.' . $class->getQuotedColumnName($identifier, $this->_platform); + } } - return $sql; + return ' ' . $sql; } /** diff --git a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php index 757d542ad..e80e78144 100644 --- a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php +++ b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php @@ -53,6 +53,7 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase } $parser = new \Doctrine\ORM\Query\Parser($query); + // We do NOT test SQL output here. That only unnecessarily slows down the tests! $parser->setCustomOutputTreeWalker('Doctrine\Tests\Mocks\MockTreeWalker'); @@ -225,15 +226,25 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase $this->assertValidDql("SELECT u.name, (SELECT COUNT(p.phonenumber) FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.phonenumber = 1234) pcount FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'"); } - /*public function testSubselectInSelectPart2() + public function testArithmeticExpressionInSelectPart() { $this->assertValidDql("SELECT SUM(u.id) / COUNT(u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u"); - }*/ + } - /*public function testSubselectInSelectPart3() + public function testArithmeticExpressionInSubselectPart() { $this->assertValidDql("SELECT (SELECT SUM(u.id) / COUNT(u.id) FROM Doctrine\Tests\Models\CMS\CmsUser u2) value FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'"); - }*/ + } + + public function testArithmeticExpressionWithParenthesisInSubselectPart() + { + $this->assertValidDql("SELECT (SELECT (SUM(u.id) / COUNT(u.id)) FROM Doctrine\Tests\Models\CMS\CmsUser u2) value FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'"); + } + + public function testDuplicateAliasInSubselectPart() + { + $this->assertInvalidDql("SELECT (SELECT SUM(u.id) / COUNT(u.id) AS foo FROM Doctrine\Tests\Models\CMS\CmsUser u2) foo FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = 'jon'"); + } public function testPositionalInputParameter() { @@ -377,6 +388,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 testArithmeticExpressionWithoutParenthesisInWhereClause() + { + $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE SIZE(u.phonenumbers) + 1 > 10'); + } + public function testMemberOfExpression() { $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE :param MEMBER OF u.phonenumbers'); diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 7a664b400..24be336a3 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -102,6 +102,33 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } + public function testSelectCorrelatedSubqueryComplexMathematicalExpression() + { + $this->assertSqlGeneration( + 'SELECT (SELECT (count(p.phonenumber)+5)*10 FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p JOIN p.user ui WHERE ui.id = u.id) AS c FROM Doctrine\Tests\Models\CMS\CmsUser u', + 'SELECT (SELECT (count(c0_.phonenumber) + 5) * 10 AS sclr1 FROM cms_phonenumbers c0_ INNER JOIN cms_users c1_ ON c0_.user_id = c1_.id WHERE c1_.id = c2_.id) AS sclr0 FROM cms_users c2_' + ); + } + + public function testSelectComplexMathematicalExpression() + { + $this->assertSqlGeneration( + 'SELECT (count(p.phonenumber)+5)*10 FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p JOIN p.user ui WHERE ui.id = ?1', + 'SELECT (count(c0_.phonenumber) + 5) * 10 AS sclr0 FROM cms_phonenumbers c0_ INNER JOIN cms_users c1_ ON c0_.user_id = c1_.id WHERE c1_.id = ?' + ); + } + + /* NOT (YET?) SUPPORTED. + Can be supported if SimpleSelectExpresion supports SingleValuedPathExpression instead of StateFieldPathExpression. + + public function testSingleAssociationPathExpressionInSubselect() + { + $this->assertSqlGeneration( + 'SELECT (SELECT p.user FROM Doctrine\Tests\Models\CMS\CmsPhonenumber p WHERE p.user = u) user_id FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1', + 'SELECT (SELECT c0_.user_id FROM cms_phonenumbers c0_ WHERE c0_.user_id = c1_.id) AS sclr0 FROM cms_users c1_ WHERE c1_.id = ?' + ); + }*/ + public function testSupportsOrderByWithAscAsDefault() { $this->assertSqlGeneration( From 496a34a4d246f7b3e628e37963d8950e43540733 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Thu, 12 Aug 2010 00:16:07 -0300 Subject: [PATCH 2/4] [DDC-581] Implemented support to SingleValuedPathExpression to InExpression. --- lib/Doctrine/ORM/Query/Parser.php | 4 +- .../ORM/Query/LanguageRecognitionTest.php | 10 ++++ .../ORM/Query/SelectSqlGenerationTest.php | 58 +++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 5015394f7..8aa82e9dd 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -2421,13 +2421,13 @@ class Parser } /** - * InExpression ::= StateFieldPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")" + * InExpression ::= SingleValuedPathExpression ["NOT"] "IN" "(" (InParameter {"," InParameter}* | Subselect) ")" * * @return \Doctrine\ORM\Query\AST\InExpression */ public function InExpression() { - $inExpression = new AST\InExpression($this->StateFieldPathExpression()); + $inExpression = new AST\InExpression($this->SingleValuedPathExpression()); if ($this->_lexer->isNextToken(Lexer::T_NOT)) { $this->match(Lexer::T_NOT); diff --git a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php index e80e78144..aab94c9dc 100644 --- a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php +++ b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php @@ -135,6 +135,16 @@ class LanguageRecognitionTest extends \Doctrine\Tests\OrmTestCase $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id NOT IN (1)'); } + public function testInExpressionWithSingleValuedAssociationPathExpression() + { + $this->assertValidDql("SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u WHERE u.avatar IN (?1, ?2)"); + } + + public function testInvalidInExpressionWithCollectionValuedAssociationPathExpression() + { + $this->assertInvalidDql("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.phonenumbers IN (?1, ?2)"); + } + public function testInstanceOfExpressionSupportedInWherePart() { $this->assertValidDql('SELECT u FROM Doctrine\Tests\Models\Company\CompanyPerson u WHERE u INSTANCE OF Doctrine\Tests\Models\Company\CompanyEmployee'); diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 24be336a3..2e0ff090d 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -15,6 +15,14 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase $this->_em = $this->_getTestEntityManager(); } + /** + * Assert a valid SQL generation. + * + * @param string $dqlToBeTested + * @param string $sqlToBeConfirmed + * @param array $queryHints + * @param array $queryParams + */ public function assertSqlGeneration($dqlToBeTested, $sqlToBeConfirmed, array $queryHints = array(), array $queryParams = array()) { try { @@ -38,6 +46,39 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase } } + /** + * Asser an invalid SQL generation. + * + * @param string $dqlToBeTested + * @param string $expectedException + * @param array $queryHints + * @param array $queryParams + */ + public function assertInvalidSqlGeneration($dqlToBeTested, $expectedException, array $queryHints = array(), array $queryParams = array()) + { + $this->setExpectedException($expectedException); + + $query = $this->_em->createQuery($dqlToBeTested); + + foreach ($queryParams AS $name => $value) { + $query->setParameter($name, $value); + } + + $query->setHint(Query::HINT_FORCE_PARTIAL_LOAD, true) + ->useQueryCache(false); + + foreach ($queryHints AS $name => $value) { + $query->setHint($name, $value); + } + + $sql = $query->getSql(); + $query->free(); + + // If we reached here, test failed + $this->fail($sql); + } + + public function testSupportsSelectForAllFields() { $this->assertSqlGeneration( @@ -374,6 +415,23 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } + public function testInExpressionWithSingleValuedAssociationPathExpressionInWherePart() + { + $this->assertSqlGeneration( + 'SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u WHERE u.avatar IN (?1, ?2)', + 'SELECT f0_.id AS id0, f0_.username AS username1 FROM forum_users f0_ WHERE f0_.avatar_id IN (?, ?)' + ); + } + + public function testInvalidInExpressionWithSingleValuedAssociationPathExpressionOnInverseSide() + { + // We do not support SingleValuedAssociationPathExpression on inverse side + $this->assertInvalidSqlGeneration( + "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.address IN (?1, ?2)", + "Doctrine\ORM\Query\QueryException" + ); + } + public function testSupportsConcatFunctionForMysqlAndPostgresql() { $connMock = $this->_em->getConnection(); From 5719f8523bbb3c8a6fd11c749e5317b238d3d2b3 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Thu, 12 Aug 2010 01:07:48 -0300 Subject: [PATCH 3/4] [DDC-577] Updated allocationSize to 1 based on discussion on DDC-569. --- lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php index bc11daa3d..7da64480b 100644 --- a/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php +++ b/lib/Doctrine/ORM/Mapping/ClassMetadataFactory.php @@ -393,7 +393,7 @@ class ClassMetadataFactory if ( ! $definition) { $sequenceName = $class->getTableName() . '_' . $class->getSingleIdentifierColumnName() . '_seq'; $definition['sequenceName'] = $this->_targetPlatform->fixSchemaElementName($sequenceName); - $definition['allocationSize'] = 10; + $definition['allocationSize'] = 1; $definition['initialValue'] = 1; $class->setSequenceGeneratorDefinition($definition); } From d56d11845836876a78d533b6c0e627264fdf495c Mon Sep 17 00:00:00 2001 From: "Roman S. Borschel" Date: Sat, 14 Aug 2010 19:10:13 +0200 Subject: [PATCH 4/4] Fixed field access. --- lib/Doctrine/ORM/Tools/EntityGenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/Doctrine/ORM/Tools/EntityGenerator.php b/lib/Doctrine/ORM/Tools/EntityGenerator.php index 8e353bda9..b32197895 100644 --- a/lib/Doctrine/ORM/Tools/EntityGenerator.php +++ b/lib/Doctrine/ORM/Tools/EntityGenerator.php @@ -513,7 +513,7 @@ public function () $methods[] = $code; } } else if ($associationMapping['type'] == ClassMetadataInfo::ONE_TO_MANY) { - if ($associationMapping->isOwningSide) { + if ($associationMapping['isOwningSide']) { if ($code = $this->_generateEntityStubMethod($metadata, 'set', $associationMapping['fieldName'], $associationMapping['targetEntity'])) { $methods[] = $code; }