From 4ea35c58e7b07c4cd862b28d19a9b2780fe4123a Mon Sep 17 00:00:00 2001 From: Michael Moravec Date: Wed, 4 Jul 2018 20:32:27 +0200 Subject: [PATCH] Fix #7286: StringPrimary no longer accepts aggregate functions as argument --- lib/Doctrine/ORM/Query/Parser.php | 4 + .../ORM/Functional/Ticket/GH7286Test.php | 141 ++++++++++++++++++ .../ORM/Query/LanguageRecognitionTest.php | 7 + 3 files changed, 152 insertions(+) create mode 100644 tests/Doctrine/Tests/ORM/Functional/Ticket/GH7286Test.php diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 48b13df64a..d52f3ce1b2 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -2924,6 +2924,10 @@ public function StringPrimary() case Lexer::T_COALESCE: case Lexer::T_NULLIF: return $this->CaseExpression(); + default: + if ($this->isAggregateFunction($lookaheadType)) { + return $this->AggregateExpression(); + } } $this->syntaxError( diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7286Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7286Test.php new file mode 100644 index 0000000000..e7c29aa8c8 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/GH7286Test.php @@ -0,0 +1,141 @@ +setUpEntitySchema( + [ + GH7286Entity::class, + ] + ); + + $entityA = new GH7286Entity('foo', 1); + $entityB = new GH7286Entity('foo', 2); + $entityC = new GH7286Entity('bar', 3); + + $this->_em->persist($entityA); + $this->_em->persist($entityB); + $this->_em->persist($entityC); + $this->_em->flush(); + $this->_em->clear(); + } + + public function testAggregateExpressionInFunction() : void + { + $query = $this->_em->createQuery( + 'SELECT CONCAT(e.type, MIN(e.version)) pair' + . ' FROM ' . GH7286Entity::class . ' e' + . ' GROUP BY e.type' + . ' ORDER BY e.type' + ); + + self::assertSame( + [ + ['pair' => 'bar3'], + ['pair' => 'foo1'], + ], + $query->getArrayResult() + ); + } + + /** + * @group DDC-1091 + */ + public function testAggregateFunctionInCustomFunction() + { + $this->_em->getConfiguration()->addCustomStringFunction('CC', GH7286CustomConcat::class); + + $entityA = new GH7286Entity('foo', 1); + $this->_em->persist($entityA); + $this->_em->flush(); + $this->_em->clear(); + + $query = $this->_em->createQuery( + 'SELECT CC(e.type, MIN(e.version)) pair' + . ' FROM ' . GH7286Entity::class . ' e' + . ' WHERE e.type = :type' + ); + $query->setParameter('type', 'foo'); + + self::assertSame( + ['pair' => 'foo1'], + $query->getSingleResult() + ); + } +} + +/** + * @Entity + */ +class GH7286Entity +{ + /** + * @Id + * @Column(type="integer") + * @GeneratedValue + * @var int + */ + public $id; + + /** + * @Column + * @var string + */ + public $type; + + /** + * @Column(type="integer") + * @var int + */ + public $version; + + public function __construct(string $type, int $version) + { + $this->type = $type; + $this->version = $version; + } +} + +class GH7286CustomConcat extends FunctionNode +{ + /** @var Node */ + private $first; + + /** @var Node */ + private $second; + + public function parse(Parser $parser): void + { + $parser->match(Lexer::T_IDENTIFIER); + $parser->match(Lexer::T_OPEN_PARENTHESIS); + + $this->first = $parser->StringPrimary(); + $parser->match(Lexer::T_COMMA); + $this->second = $parser->StringPrimary(); + + $parser->match(Lexer::T_CLOSE_PARENTHESIS); + } + + public function getSql(SqlWalker $walker): string + { + return $walker->getConnection()->getDatabasePlatform()->getConcatExpression( + $this->first->dispatch($walker), + $this->second->dispatch($walker) + ); + } +} diff --git a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php index 064c0a799c..fe3b298cbf 100644 --- a/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php +++ b/tests/Doctrine/Tests/ORM/Query/LanguageRecognitionTest.php @@ -708,6 +708,13 @@ public function testNewLiteralWithSubselectExpression() { $this->assertValidDQL("SELECT new " . __NAMESPACE__ . "\\DummyStruct(u.id, 'foo', (SELECT 1 FROM Doctrine\Tests\Models\CMS\CmsUser su), true) FROM Doctrine\Tests\Models\CMS\CmsUser u"); } + + public function testStringPrimaryAcceptsAggregateExpression() : void + { + $this->assertValidDQL( + 'SELECT CONCAT(a.topic, MAX(a.version)) last FROM Doctrine\Tests\Models\CMS\CmsArticle a GROUP BY a' + ); + } } /** @Entity */