diff --git a/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php b/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php index 53f064a90..66c27efab 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/ModFunction.php @@ -53,7 +53,7 @@ class ModFunction extends FunctionNode */ public function parse(\Doctrine\ORM\Query\Parser $parser) { - $parser->match(Lexer::T_MOD); + $parser->match(Lexer::T_IDENTIFIER); $parser->match(Lexer::T_OPEN_PARENTHESIS); $this->firstSimpleArithmeticExpression = $parser->SimpleArithmeticExpression(); diff --git a/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php b/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php index 52173c406..1a7c1a150 100644 --- a/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php +++ b/lib/Doctrine/ORM/Query/AST/Functions/SizeFunction.php @@ -109,7 +109,7 @@ class SizeFunction extends FunctionNode */ public function parse(\Doctrine\ORM\Query\Parser $parser) { - $parser->match(Lexer::T_SIZE); + $parser->match(Lexer::T_IDENTIFIER); $parser->match(Lexer::T_OPEN_PARENTHESIS); $this->collectionPathExpression = $parser->CollectionValuedPathExpression(); diff --git a/lib/Doctrine/ORM/Query/Lexer.php b/lib/Doctrine/ORM/Query/Lexer.php index 1eef6afeb..0000b3fe2 100644 --- a/lib/Doctrine/ORM/Query/Lexer.php +++ b/lib/Doctrine/ORM/Query/Lexer.php @@ -98,18 +98,16 @@ class Lexer extends \Doctrine\Common\Lexer const T_OUTER = 144; const T_SELECT = 145; const T_SET = 146; - const T_SIZE = 147; - const T_SOME = 148; - const T_SUM = 149; - const T_THEN = 150; - const T_TRAILING = 151; - const T_TRUE = 152; - const T_UPDATE = 153; - const T_WHEN = 154; - const T_WHERE = 155; - const T_WITH = 156; - const T_PARTIAL = 157; - const T_MOD = 158; + const T_SOME = 147; + const T_SUM = 148; + const T_THEN = 149; + const T_TRAILING = 150; + const T_TRUE = 151; + const T_UPDATE = 152; + const T_WHEN = 153; + const T_WHERE = 154; + const T_WITH = 155; + const T_PARTIAL = 156; /** * Creates a new query scanner object. @@ -205,4 +203,4 @@ class Lexer extends \Doctrine\Common\Lexer return $type; } -} \ No newline at end of file +} diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 7b9a67135..f8c8d1c4c 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -416,9 +416,10 @@ class Parser /** * Peek beyond the matched closing parenthesis and return the first token after that one. * + * @param boolean $resetPeek Reset peek after finding the closing parenthesis * @return array */ - private function _peekBeyondClosingParenthesis() + private function _peekBeyondClosingParenthesis($resetPeek = true) { $token = $this->_lexer->peek(); $numUnmatched = 1; @@ -440,7 +441,9 @@ class Parser $token = $this->_lexer->peek(); } - $this->_lexer->resetPeek(); + if ($resetPeek) { + $this->_lexer->resetPeek(); + } return $token; } @@ -541,7 +544,7 @@ class Parser foreach ($expr->partialFieldSet as $field) { if (isset($class->fieldMappings[$field])) { - continue; + continue; } $this->semanticalError( @@ -1335,7 +1338,7 @@ class Parser break; case ($glimpse['type'] === Lexer::T_DOT): $expr = $this->SingleValuedPathExpression(); - + break; case ($this->_lexer->peek() && $this->_isMathOperator($this->_peekBeyondClosingParenthesis())): $expr = $this->ScalarExpression(); @@ -2206,7 +2209,13 @@ class Parser if ($peek['value'] == '(') { // Peek beyond the matching closing paranthesis ')' $this->_lexer->peek(); - $token = $this->_peekBeyondClosingParenthesis(); + $token = $this->_peekBeyondClosingParenthesis(false); + + if ($token['type'] === Lexer::T_NOT) { + $token = $this->_lexer->peek(); + } + + $this->_lexer->resetPeek(); } else { // Peek beyond the PathExpression (or InputParameter) $peek = $this->_lexer->peek(); @@ -2658,7 +2667,7 @@ class Parser ? $this->SingleValuedPathExpression() : $this->SimpleArithmeticExpression(); - $this->match(Lexer::T_CLOSE_PARENTHESIS); + $this->match(Lexer::T_CLOSE_PARENTHESIS); return new AST\AggregateExpression($functionName, $pathExp, $isDistinct); } diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 40bbbaab7..9a998580b 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -374,6 +374,17 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } + /** + * @group DDC-1802 + */ + public function testSupportsNotBetweenForSizeFunction() + { + $this->assertSqlGeneration( + "SELECT m.name FROM Doctrine\Tests\Models\StockExchange\Market m WHERE SIZE(m.stocks) NOT BETWEEN ?1 AND ?2", + "SELECT e0_.name AS name0 FROM exchange_markets e0_ WHERE (SELECT COUNT(*) FROM exchange_stocks e1_ WHERE e1_.market_id = e0_.id) NOT BETWEEN ? AND ?" + ); + } + public function testSupportsFunctionalExpressionsInWherePart() { $this->assertSqlGeneration( @@ -472,6 +483,17 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } + /** + * @group DDC-1802 + */ + public function testSupportsNotInExpressionForModFunction() + { + $this->assertSqlGeneration( + "SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE MOD(u.id, 5) NOT IN(1,3,4)", + "SELECT c0_.name AS name0 FROM cms_users c0_ WHERE MOD(c0_.id, 5) NOT IN (1, 3, 4)" + ); + } + public function testInExpressionWithSingleValuedAssociationPathExpressionInWherePart() { $this->assertSqlGeneration( @@ -854,6 +876,26 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase ); } + /** + * @group DDC-1802 + */ + public function testStringFunctionNotLikeExpression() + { + $this->assertSqlGeneration( + "SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE LOWER(u.name) NOT LIKE '%foo OR bar%'", + "SELECT c0_.name AS name0 FROM cms_users c0_ WHERE LOWER(c0_.name) NOT LIKE '%foo OR bar%'" + ); + + $this->assertSqlGeneration( + "SELECT u.name FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE UPPER(LOWER(u.name)) NOT LIKE UPPER(LOWER(:str))", + "SELECT c0_.name AS name0 FROM cms_users c0_ WHERE UPPER(LOWER(c0_.name)) NOT LIKE UPPER(LOWER(?))" + ); + $this->assertSqlGeneration( + "SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a WITH a.topic NOT LIKE u.name", + "SELECT c0_.id AS id0, c0_.status AS status1, c0_.username AS username2, c0_.name AS name3 FROM cms_users c0_ LEFT JOIN cms_articles c1_ ON c0_.id = c1_.user_id AND (c1_.topic NOT LIKE c0_.name)" + ); + } + /** * @group DDC-338 */