From 664e51db20fe6f1b01135561847b57051eecc24f Mon Sep 17 00:00:00 2001 From: "m.guihini" Date: Tue, 23 May 2023 11:25:11 +0000 Subject: [PATCH 1/3] Adding ORDER support for UNION queries --- src/SQLParser/Query/StatementFactory.php | 10 ++++++- src/SQLParser/Query/Union.php | 33 ++++++++++++++++++++++++ tests/Mouf/Database/MagicQueryTest.php | 12 +++++++++ 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/SQLParser/Query/StatementFactory.php b/src/SQLParser/Query/StatementFactory.php index e5cebaf..ff50022 100644 --- a/src/SQLParser/Query/StatementFactory.php +++ b/src/SQLParser/Query/StatementFactory.php @@ -96,7 +96,15 @@ public static function toObject(array $desc) /** @var Select[] $selects */ $selects = array_map([self::class, 'toObject'], $desc['UNION']); - return new Union($selects); + $union = new Union($selects); + + if (isset($desc['0']) && isset($desc['0']['ORDER'])) { + $order = NodeFactory::mapArrayToNodeObjectList($desc['0']['ORDER']); + $order = NodeFactory::simplify($order); + $union->setOrder($order); + } + + return $union; } else { throw new \BadMethodCallException('Unknown query'); } diff --git a/src/SQLParser/Query/Union.php b/src/SQLParser/Query/Union.php index 255ecf3..aef2ac3 100644 --- a/src/SQLParser/Query/Union.php +++ b/src/SQLParser/Query/Union.php @@ -34,6 +34,29 @@ public function __construct(array $selects) $this->selects = $selects; } + /** @var NodeInterface[]|NodeInterface */ + private $order; + + /** + * Returns the list of order statements. + * + * @return NodeInterface[]|NodeInterface + */ + public function getOrder() + { + return $this->order; + } + + /** + * Sets the list of order statements. + * + * @param NodeInterface[]|NodeInterface $order + */ + public function setOrder($order): void + { + $this->order = $order; + } + /** * @param MoufManager $moufManager * @@ -43,6 +66,7 @@ public function toInstanceDescriptor(MoufManager $moufManager) { $instanceDescriptor = $moufManager->createInstance(get_called_class()); $instanceDescriptor->getProperty('selects')->setValue(NodeFactory::nodeToInstanceDescriptor($this->selects, $moufManager)); + $instanceDescriptor->getProperty('order')->setValue(NodeFactory::nodeToInstanceDescriptor($this->order, $moufManager)); return $instanceDescriptor; } @@ -59,6 +83,7 @@ public function overwriteInstanceDescriptor($name, MoufManager $moufManager) //$name = $moufManager->findInstanceName($this); $instanceDescriptor = $moufManager->getInstanceDescriptor($name); $instanceDescriptor->getProperty('selects')->setValue(NodeFactory::nodeToInstanceDescriptor($this->selects, $moufManager)); + $instanceDescriptor->getProperty('order')->setValue(NodeFactory::nodeToInstanceDescriptor($this->order, $moufManager)); return $instanceDescriptor; } @@ -82,6 +107,13 @@ public function toSql(array $parameters, AbstractPlatform $platform, int $indent $sql = implode(' UNION ', $selectsSql); + if (!empty($this->order)) { + $order = NodeFactory::toSql($this->order, $platform, $parameters, ',', false, $indent + 2, $conditionsMode, $extrapolateParameters); + if ($order) { + $sql .= "\nORDER BY ".$order; + } + } + return $sql; } @@ -99,6 +131,7 @@ public function walk(VisitorInterface $visitor) } if ($result !== NodeTraverser::DONT_TRAVERSE_CHILDREN) { $this->walkChildren($this->selects, $visitor); + $this->walkChildren($this->order, $visitor); } return $visitor->leaveNode($node); diff --git a/tests/Mouf/Database/MagicQueryTest.php b/tests/Mouf/Database/MagicQueryTest.php index 3c5e789..1c4a00d 100644 --- a/tests/Mouf/Database/MagicQueryTest.php +++ b/tests/Mouf/Database/MagicQueryTest.php @@ -478,4 +478,16 @@ public function testPrepareStatementCache(): void $this->assertEquals('SELECT * FROM users WHERE 0 <> 0', self::simplifySql($magicQuery->buildPreparedStatement('SELECT * FROM users WHERE id IN :users', ['users' => []]))); $this->assertEquals('SELECT * FROM users WHERE id IN (:users)', self::simplifySql($magicQuery->buildPreparedStatement('SELECT * FROM users WHERE id IN :users', ['users' => [1]]))); } + + public function testUnionAndOrderBy(): void + { + $magicQuery = new MagicQuery(null, new ArrayCache()); + + $query = '(SELECT id FROM users) UNION (SELECT id FROM users) ORDER BY users.id ASC'; + + $this->assertEquals( + 'SELECT id FROM users UNION SELECT id FROM users ORDER BY users.id ASC', + self::simplifySql($magicQuery->buildPreparedStatement($query)) + ); + } } From 7fac2293bab27d2afe1d8a74d834482c8bb05c51 Mon Sep 17 00:00:00 2001 From: "m.guihini" Date: Tue, 23 May 2023 12:29:21 +0000 Subject: [PATCH 2/3] fix Union class for ORDER support --- src/SQLParser/Query/Union.php | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/src/SQLParser/Query/Union.php b/src/SQLParser/Query/Union.php index aef2ac3..69ae181 100644 --- a/src/SQLParser/Query/Union.php +++ b/src/SQLParser/Query/Union.php @@ -137,22 +137,27 @@ public function walk(VisitorInterface $visitor) return $visitor->leaveNode($node); } - /** - * @param array $children - * @param VisitorInterface $visitor - */ - private function walkChildren(array &$children, VisitorInterface $visitor): void + private function walkChildren(&$children, VisitorInterface $visitor): void { if ($children) { - foreach ($children as $key => $operand) { - if ($operand) { - $result2 = $operand->walk($visitor); - if ($result2 === NodeTraverser::REMOVE_NODE) { - unset($children[$key]); - } elseif ($result2 instanceof NodeInterface) { - $children[$key] = $result2; + if (is_array($children)) { + foreach ($children as $key => $operand) { + if ($operand) { + $result2 = $operand->walk($visitor); + if ($result2 === NodeTraverser::REMOVE_NODE) { + unset($children[$key]); + } elseif ($result2 instanceof NodeInterface) { + $children[$key] = $result2; + } } } + } else { + $result2 = $children->walk($visitor); + if ($result2 === NodeTraverser::REMOVE_NODE) { + $children = null; + } elseif ($result2 instanceof NodeInterface) { + $children = $result2; + } } } } From e41dbc24870e69b4c6b388c16ddecafa7ef0bf67 Mon Sep 17 00:00:00 2001 From: "m.guihini" Date: Tue, 23 May 2023 16:37:00 +0000 Subject: [PATCH 3/3] refactor and add test for UNION --- src/SQLParser/Query/Union.php | 6 +++++- tests/Mouf/Database/MagicQueryTest.php | 22 ++++++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/SQLParser/Query/Union.php b/src/SQLParser/Query/Union.php index 69ae181..f92cf25 100644 --- a/src/SQLParser/Query/Union.php +++ b/src/SQLParser/Query/Union.php @@ -105,7 +105,7 @@ public function toSql(array $parameters, AbstractPlatform $platform, int $indent return $select->toSql($parameters, $platform, $indent, $conditionsMode, $extrapolateParameters); }, $this->selects); - $sql = implode(' UNION ', $selectsSql); + $sql = '(' . implode(') UNION (', $selectsSql) . ')'; if (!empty($this->order)) { $order = NodeFactory::toSql($this->order, $platform, $parameters, ',', false, $indent + 2, $conditionsMode, $extrapolateParameters); @@ -137,6 +137,10 @@ public function walk(VisitorInterface $visitor) return $visitor->leaveNode($node); } + /** + * @param array|NodeInterface|null $children + * @param VisitorInterface $visitor + */ private function walkChildren(&$children, VisitorInterface $visitor): void { if ($children) { diff --git a/tests/Mouf/Database/MagicQueryTest.php b/tests/Mouf/Database/MagicQueryTest.php index 1c4a00d..7aef086 100644 --- a/tests/Mouf/Database/MagicQueryTest.php +++ b/tests/Mouf/Database/MagicQueryTest.php @@ -186,7 +186,7 @@ public function testStandardSelect() $this->assertEquals('SELECT COUNT(DISTINCT a, b) FROM users', self::simplifySql($magicQuery->build($sql))); $sql = 'SELECT a FROM users UNION SELECT a FROM users'; - $this->assertEquals('SELECT a FROM users UNION SELECT a FROM users', self::simplifySql($magicQuery->build($sql))); + $this->assertEquals('(SELECT a FROM users) UNION (SELECT a FROM users)', self::simplifySql($magicQuery->build($sql))); $sql = 'SELECT a FROM users u, users u2'; $this->assertEquals('SELECT a FROM users u CROSS JOIN users u2', self::simplifySql($magicQuery->build($sql))); @@ -486,7 +486,25 @@ public function testUnionAndOrderBy(): void $query = '(SELECT id FROM users) UNION (SELECT id FROM users) ORDER BY users.id ASC'; $this->assertEquals( - 'SELECT id FROM users UNION SELECT id FROM users ORDER BY users.id ASC', + '(SELECT id FROM users) UNION (SELECT id FROM users) ORDER BY users.id ASC', + self::simplifySql($magicQuery->buildPreparedStatement($query)) + ); + + $query = '(SELECT id FROM users) UNION (SELECT id FROM users ORDER BY users.id ASC)'; + $this->assertEquals( + '(SELECT id FROM users) UNION (SELECT id FROM users ORDER BY users.id ASC)', + self::simplifySql($magicQuery->buildPreparedStatement($query)) + ); + + $query = '(SELECT id FROM users ORDER BY users.id ASC) UNION (SELECT id FROM users)'; + $this->assertEquals( + '(SELECT id FROM users ORDER BY users.id ASC) UNION (SELECT id FROM users)', + self::simplifySql($magicQuery->buildPreparedStatement($query)) + ); + + $query = 'SELECT id FROM users UNION SELECT id FROM users ORDER BY users.id ASC'; + $this->assertEquals( + '(SELECT id FROM users) UNION (SELECT id FROM users) ORDER BY users.id ASC', self::simplifySql($magicQuery->buildPreparedStatement($query)) ); }