From 92ff9c91083f4f23160d780a8c5d354b294e7e52 Mon Sep 17 00:00:00 2001 From: David ALLIX Date: Mon, 27 Dec 2021 19:03:47 +0100 Subject: [PATCH] Allow arithmetic expressions within IN operator (#9242) * allow arithmetic expressions within IN operator Co-authored-by: Artem Stepin --- .../reference/dql-doctrine-query-language.rst | 2 +- lib/Doctrine/ORM/Query/Parser.php | 6 ++-- lib/Doctrine/ORM/Query/SqlWalker.php | 2 +- .../ORM/Functional/QueryDqlFunctionTest.php | 33 +++++++++++++++++++ .../ORM/Query/SelectSqlGenerationTest.php | 14 ++++++++ 5 files changed, 52 insertions(+), 5 deletions(-) diff --git a/docs/en/reference/dql-doctrine-query-language.rst b/docs/en/reference/dql-doctrine-query-language.rst index 632d729ffb8..24488dec264 100644 --- a/docs/en/reference/dql-doctrine-query-language.rst +++ b/docs/en/reference/dql-doctrine-query-language.rst @@ -1724,7 +1724,7 @@ Literal Values .. code-block:: php Literal ::= string | char | integer | float | boolean - InParameter ::= Literal | InputParameter + InParameter ::= ArithmeticExpression | InputParameter Input Parameter ~~~~~~~~~~~~~~~ diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 3e5bcf4d1b5..baab67c0800 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -2758,9 +2758,9 @@ public function Literal() } /** - * InParameter ::= Literal | InputParameter + * InParameter ::= ArithmeticExpression | InputParameter * - * @return AST\InputParameter|AST\Literal + * @return AST\InputParameter|AST\ArithmeticExpression */ public function InParameter() { @@ -2768,7 +2768,7 @@ public function InParameter() return $this->InputParameter(); } - return $this->Literal(); + return $this->ArithmeticExpression(); } /** diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 2a1139181cb..d896f857285 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -2106,7 +2106,7 @@ public function walkInParameter($inParam) { return $inParam instanceof AST\InputParameter ? $this->walkInputParameter($inParam) - : $this->walkLiteral($inParam); + : $this->walkArithmeticExpression($inParam); } /** diff --git a/tests/Doctrine/Tests/ORM/Functional/QueryDqlFunctionTest.php b/tests/Doctrine/Tests/ORM/Functional/QueryDqlFunctionTest.php index 316d47d8656..745cec914bf 100644 --- a/tests/Doctrine/Tests/ORM/Functional/QueryDqlFunctionTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/QueryDqlFunctionTest.php @@ -420,6 +420,39 @@ public function testBitAndComparison(): void self::assertEquals($result[3][0]['salary'] / 100000 & 2, $result[3]['salary_bit_and']); } + public function testInArithmeticExpression1(): void + { + $dql = <<<'SQL' + SELECT m, m.name AS m_name + FROM Doctrine\Tests\Models\Company\CompanyManager m + WHERE m.salary IN (800000 / 8, 100000 * 2) +SQL; + + $result = $this->_em->createQuery($dql)->getArrayResult(); + + self::assertCount(2, $result); + self::assertEquals('Roman B.', $result[0]['m_name']); + self::assertEquals('Benjamin E.', $result[1]['m_name']); + } + + public function testInArithmeticExpression2(): void + { + $this->_em->getConfiguration()->addCustomStringFunction('FOO', static function ($funcName) { + return new NoOp($funcName); // See Doctrine/Tests/ORM/Functional/CustomFunctionsTest + }); + + $dql = <<<'SQL' + SELECT m, m.name AS m_name + FROM Doctrine\Tests\Models\Company\CompanyManager m + WHERE m.department IN (FOO('Administration')) +SQL; + + $result = $this->_em->createQuery($dql)->getArrayResult(); + + self::assertCount(1, $result); + self::assertEquals('Jonathan W.', $result[0]['m_name']); + } + protected function generateFixture(): void { $manager1 = new CompanyManager(); diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 710a03accbf..efc1cd94c18 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -643,6 +643,20 @@ public function testInvalidInExpressionWithSingleValuedAssociationPathExpression ); } + public function testInExpressionWithArithmeticExpression(): void + { + $this->entityManager->getConfiguration()->addCustomStringFunction('FOO', MyAbsFunction::class); + + $this->assertSqlGeneration( + "SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u WHERE u.username IN (FOO('Lo'), 'Lo', :name)", + "SELECT f0_.id AS id_0, f0_.username AS username_1 FROM forum_users f0_ WHERE f0_.username IN (ABS('Lo'), 'Lo', ?)" + ); + $this->assertSqlGeneration( + 'SELECT u FROM Doctrine\Tests\Models\Forum\ForumUser u WHERE u.id IN (1 + 1)', + 'SELECT f0_.id AS id_0, f0_.username AS username_1 FROM forum_users f0_ WHERE f0_.id IN (1 + 1)' + ); + } + public function testSupportsConcatFunctionForMysqlAndPostgresql(): void { $connMock = $this->entityManager->getConnection();