Skip to content

Commit a4e2c54

Browse files
mhtghnhomersimpsons
authored andcommitted
Adding ORDER support for UNION queries (#86)
1 parent 37cf04d commit a4e2c54

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
@@ -187,7 +187,7 @@ public function testStandardSelect()
187187
$this->assertEquals('SELECT COUNT(DISTINCT a, b) FROM users', self::simplifySql($magicQuery->build($sql)));
188188

189189
$sql = 'SELECT a FROM users UNION SELECT a FROM users';
190-
$this->assertEquals('SELECT a FROM users UNION SELECT a FROM users', self::simplifySql($magicQuery->build($sql)));
190+
$this->assertEquals('(SELECT a FROM users) UNION (SELECT a FROM users)', self::simplifySql($magicQuery->build($sql)));
191191

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

0 commit comments

Comments
 (0)