Skip to content

Commit 5107ebf

Browse files
authored
Adding ORDER support for UNION queries (#86)
1 parent 7efa677 commit 5107ebf

File tree

3 files changed

+92
-12
lines changed

3 files changed

+92
-12
lines changed

src/SQLParser/Query/StatementFactory.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,15 @@ public static function toObject(array $desc)
9696
/** @var Select[] $selects */
9797
$selects = array_map([self::class, 'toObject'], $desc['UNION']);
9898

99-
return new Union($selects);
99+
$union = new Union($selects);
100+
101+
if (isset($desc['0']) && isset($desc['0']['ORDER'])) {
102+
$order = NodeFactory::mapArrayToNodeObjectList($desc['0']['ORDER']);
103+
$order = NodeFactory::simplify($order);
104+
$union->setOrder($order);
105+
}
106+
107+
return $union;
100108
} else {
101109
throw new \BadMethodCallException('Unknown query');
102110
}

src/SQLParser/Query/Union.php

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,29 @@ public function __construct(array $selects)
3434
$this->selects = $selects;
3535
}
3636

37+
/** @var NodeInterface[]|NodeInterface */
38+
private $order;
39+
40+
/**
41+
* Returns the list of order statements.
42+
*
43+
* @return NodeInterface[]|NodeInterface
44+
*/
45+
public function getOrder()
46+
{
47+
return $this->order;
48+
}
49+
50+
/**
51+
* Sets the list of order statements.
52+
*
53+
* @param NodeInterface[]|NodeInterface $order
54+
*/
55+
public function setOrder($order): void
56+
{
57+
$this->order = $order;
58+
}
59+
3760
/**
3861
* @param MoufManager $moufManager
3962
*
@@ -43,6 +66,7 @@ public function toInstanceDescriptor(MoufManager $moufManager)
4366
{
4467
$instanceDescriptor = $moufManager->createInstance(get_called_class());
4568
$instanceDescriptor->getProperty('selects')->setValue(NodeFactory::nodeToInstanceDescriptor($this->selects, $moufManager));
69+
$instanceDescriptor->getProperty('order')->setValue(NodeFactory::nodeToInstanceDescriptor($this->order, $moufManager));
4670

4771
return $instanceDescriptor;
4872
}
@@ -59,6 +83,7 @@ public function overwriteInstanceDescriptor($name, MoufManager $moufManager)
5983
//$name = $moufManager->findInstanceName($this);
6084
$instanceDescriptor = $moufManager->getInstanceDescriptor($name);
6185
$instanceDescriptor->getProperty('selects')->setValue(NodeFactory::nodeToInstanceDescriptor($this->selects, $moufManager));
86+
$instanceDescriptor->getProperty('order')->setValue(NodeFactory::nodeToInstanceDescriptor($this->order, $moufManager));
6287

6388
return $instanceDescriptor;
6489
}
@@ -80,7 +105,14 @@ public function toSql(array $parameters, AbstractPlatform $platform, int $indent
80105
return $select->toSql($parameters, $platform, $indent, $conditionsMode, $extrapolateParameters);
81106
}, $this->selects);
82107

83-
$sql = implode(' UNION ', $selectsSql);
108+
$sql = '(' . implode(') UNION (', $selectsSql) . ')';
109+
110+
if (!empty($this->order)) {
111+
$order = NodeFactory::toSql($this->order, $platform, $parameters, ',', false, $indent + 2, $conditionsMode, $extrapolateParameters);
112+
if ($order) {
113+
$sql .= "\nORDER BY ".$order;
114+
}
115+
}
84116

85117
return $sql;
86118
}
@@ -99,27 +131,37 @@ public function walk(VisitorInterface $visitor)
99131
}
100132
if ($result !== NodeTraverser::DONT_TRAVERSE_CHILDREN) {
101133
$this->walkChildren($this->selects, $visitor);
134+
$this->walkChildren($this->order, $visitor);
102135
}
103136

104137
return $visitor->leaveNode($node);
105138
}
106139

107140
/**
108-
* @param array<Select|null> $children
141+
* @param array<Select|NodeInterface|null>|NodeInterface|null $children
109142
* @param VisitorInterface $visitor
110143
*/
111-
private function walkChildren(array &$children, VisitorInterface $visitor): void
144+
private function walkChildren(&$children, VisitorInterface $visitor): void
112145
{
113146
if ($children) {
114-
foreach ($children as $key => $operand) {
115-
if ($operand) {
116-
$result2 = $operand->walk($visitor);
117-
if ($result2 === NodeTraverser::REMOVE_NODE) {
118-
unset($children[$key]);
119-
} elseif ($result2 instanceof NodeInterface) {
120-
$children[$key] = $result2;
147+
if (is_array($children)) {
148+
foreach ($children as $key => $operand) {
149+
if ($operand) {
150+
$result2 = $operand->walk($visitor);
151+
if ($result2 === NodeTraverser::REMOVE_NODE) {
152+
unset($children[$key]);
153+
} elseif ($result2 instanceof NodeInterface) {
154+
$children[$key] = $result2;
155+
}
121156
}
122157
}
158+
} else {
159+
$result2 = $children->walk($visitor);
160+
if ($result2 === NodeTraverser::REMOVE_NODE) {
161+
$children = null;
162+
} elseif ($result2 instanceof NodeInterface) {
163+
$children = $result2;
164+
}
123165
}
124166
}
125167
}

tests/Mouf/Database/MagicQueryTest.php

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ public function testStandardSelect()
186186
$this->assertEquals('SELECT COUNT(DISTINCT a, b) FROM users', self::simplifySql($magicQuery->build($sql)));
187187

188188
$sql = 'SELECT a FROM users UNION SELECT a FROM users';
189-
$this->assertEquals('SELECT a FROM users UNION SELECT a FROM users', self::simplifySql($magicQuery->build($sql)));
189+
$this->assertEquals('(SELECT a FROM users) UNION (SELECT a FROM users)', self::simplifySql($magicQuery->build($sql)));
190190

191191
$sql = 'SELECT a FROM users u, users u2';
192192
$this->assertEquals('SELECT a FROM users u CROSS JOIN users u2', self::simplifySql($magicQuery->build($sql)));
@@ -478,4 +478,34 @@ public function testPrepareStatementCache(): void
478478
$this->assertEquals('SELECT * FROM users WHERE 0 <> 0', self::simplifySql($magicQuery->buildPreparedStatement('SELECT * FROM users WHERE id IN :users', ['users' => []])));
479479
$this->assertEquals('SELECT * FROM users WHERE id IN (:users)', self::simplifySql($magicQuery->buildPreparedStatement('SELECT * FROM users WHERE id IN :users', ['users' => [1]])));
480480
}
481+
482+
public function testUnionAndOrderBy(): void
483+
{
484+
$magicQuery = new MagicQuery(null, new ArrayCache());
485+
486+
$query = '(SELECT id FROM users) UNION (SELECT id FROM users) ORDER BY users.id ASC';
487+
488+
$this->assertEquals(
489+
'(SELECT id FROM users) UNION (SELECT id FROM users) ORDER BY users.id ASC',
490+
self::simplifySql($magicQuery->buildPreparedStatement($query))
491+
);
492+
493+
$query = '(SELECT id FROM users) UNION (SELECT id FROM users ORDER BY users.id ASC)';
494+
$this->assertEquals(
495+
'(SELECT id FROM users) UNION (SELECT id FROM users ORDER BY users.id ASC)',
496+
self::simplifySql($magicQuery->buildPreparedStatement($query))
497+
);
498+
499+
$query = '(SELECT id FROM users ORDER BY users.id ASC) UNION (SELECT id FROM users)';
500+
$this->assertEquals(
501+
'(SELECT id FROM users ORDER BY users.id ASC) UNION (SELECT id FROM users)',
502+
self::simplifySql($magicQuery->buildPreparedStatement($query))
503+
);
504+
505+
$query = 'SELECT id FROM users UNION SELECT id FROM users ORDER BY users.id ASC';
506+
$this->assertEquals(
507+
'(SELECT id FROM users) UNION (SELECT id FROM users) ORDER BY users.id ASC',
508+
self::simplifySql($magicQuery->buildPreparedStatement($query))
509+
);
510+
}
481511
}

0 commit comments

Comments
 (0)