From a70c73ae3a32e2192617400894e3292c5abf9e59 Mon Sep 17 00:00:00 2001 From: Yup Date: Mon, 22 Feb 2021 21:58:06 +0200 Subject: [PATCH] Use RegEx to match if queryPart contains OR/AND (#8453) This allows fixes cases of queries that contain line feeds or tabs but do not benefit from automatic wrapping of parenthesis. --- lib/Doctrine/ORM/Query/Expr/Composite.php | 4 +- .../Functional/QueryBuilderParenthesis.php | 112 ++++++++++++++++++ 2 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 tests/Doctrine/Tests/ORM/Functional/QueryBuilderParenthesis.php diff --git a/lib/Doctrine/ORM/Query/Expr/Composite.php b/lib/Doctrine/ORM/Query/Expr/Composite.php index 27658a7ff3f..f65c6975ccb 100644 --- a/lib/Doctrine/ORM/Query/Expr/Composite.php +++ b/lib/Doctrine/ORM/Query/Expr/Composite.php @@ -22,7 +22,7 @@ use function implode; use function is_object; -use function stripos; +use function preg_match; /** * Expression class for building DQL and parts. @@ -63,7 +63,7 @@ private function processQueryPart($part) } // Fixes DDC-1237: User may have added a where item containing nested expression (with "OR" or "AND") - if (stripos($queryPart, ' OR ') !== false || stripos($queryPart, ' AND ') !== false) { + if (preg_match('/\s(OR|AND)\s/', $queryPart)) { return $this->preSeparator . $queryPart . $this->postSeparator; } diff --git a/tests/Doctrine/Tests/ORM/Functional/QueryBuilderParenthesis.php b/tests/Doctrine/Tests/ORM/Functional/QueryBuilderParenthesis.php new file mode 100644 index 00000000000..4fa21e5bbd2 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/QueryBuilderParenthesis.php @@ -0,0 +1,112 @@ +_em->getConnection()->getConfiguration()->setSQLLogger(new \Doctrine\DBAL\Logging\EchoSQLLogger); + $this->_schemaTool->createSchema( + [ + $this->_em->getClassMetadata(QueryBuilderParenthesisEntity::class), + ] + ); + } + + protected function tearDown(): void + { + parent::tearDown(); + + $this->_schemaTool->dropSchema( + [ + $this->_em->getClassMetadata(QueryBuilderParenthesisEntity::class), + ] + ); + } + + public function testParenthesisOnSingleLine(): void + { + $queryBuilder = $this->_em->createQueryBuilder(); + $queryBuilder->select('o')->from(QueryBuilderParenthesisEntity::class, 'o'); + $queryBuilder->andWhere('o.property3 = :value3')->setParameter('value3', 'x'); + $queryBuilder->andWhere('o.property1 = :value1 OR o.property2 = :value2'); + $queryBuilder->setParameter('value1', 'x'); + $queryBuilder->setParameter('value2', 'x'); + + $query = $queryBuilder->getQuery(); + $results = $query->getResult(); + $this->assertCount(0, $results); + + $dql = $query->getDQL(); + + $this->assertSame( + 'SELECT o FROM ' . QueryBuilderParenthesisEntity::class . ' o WHERE o.property3 = :value3 AND (o.property1 = :value1 OR o.property2 = :value2)', + $dql + ); + } + + public function testParenthesisOnMultiLine(): void + { + $queryBuilder = $this->_em->createQueryBuilder(); + $queryBuilder->select('o')->from(QueryBuilderParenthesisEntity::class, 'o'); + $queryBuilder->andWhere('o.property3 = :value3')->setParameter('value3', 'x'); + + $queryBuilder->andWhere( + 'o.property1 = :value1 +OR o.property2 = :value2' + ); + $queryBuilder->setParameter('value1', 'x'); + $queryBuilder->setParameter('value2', 'x'); + + $query = $queryBuilder->getQuery(); + $results = $query->getResult(); + $this->assertCount(0, $results); + + $dql = $query->getDQL(); + + $this->assertSame( + 'SELECT o FROM ' . QueryBuilderParenthesisEntity::class . ' o WHERE o.property3 = :value3 AND (o.property1 = :value1 +OR o.property2 = :value2)', + $dql + ); + } +} + + +/** + * @Entity + */ +class QueryBuilderParenthesisEntity +{ + /** + * @var int|null + * @Column(type="integer") + * @Id + * @GeneratedValue + */ + public $id; + + /** + * @var string|null + * @Column() + */ + public $property1; + + /** + * @var string|null + * @Column() + */ + public $property2; + + /** + * @var string|null + * @Column() + */ + public $property3; +}