Skip to content

Commit 6e33561

Browse files
committed
Improved support for UNION.
1 parent fc390a8 commit 6e33561

6 files changed

Lines changed: 86 additions & 7 deletions

File tree

src/Components/UnionKeyword.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
/**
4+
* `UNION` keyword builder.
5+
*
6+
* @package SqlParser
7+
* @subpackage Components
8+
*/
9+
namespace SqlParser\Components;
10+
11+
use SqlParser\Component;
12+
use SqlParser\Parser;
13+
use SqlParser\Token;
14+
use SqlParser\TokensList;
15+
16+
/**
17+
* `UNION` keyword builder.
18+
*
19+
* @category Keywords
20+
* @package SqlParser
21+
* @subpackage Components
22+
* @author Dan Ungureanu <udan1107@gmail.com>
23+
* @license http://opensource.org/licenses/GPL-2.0 GNU Public License
24+
*/
25+
class UnionKeyword extends Component
26+
{
27+
28+
/**
29+
* @param SelectStatement[] $component The component to be built.
30+
*
31+
* @return string
32+
*/
33+
public static function build($component)
34+
{
35+
$ret = array();
36+
foreach ($component as $c) {
37+
$ret[] = $c->build();
38+
}
39+
return implode(" UNION ", $ret);
40+
}
41+
}

src/Parser.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,12 @@ class Parser
114114
'field' => 'options',
115115
),
116116

117+
// This is used only for building.
118+
'UNION' => array(
119+
'class' => 'SqlParser\\Components\\UnionKeyword',
120+
'field' => 'union',
121+
),
122+
117123
'ALTER' => array(
118124
'class' => 'SqlParser\\Components\\Expression',
119125
'field' => 'table',
@@ -425,6 +431,18 @@ public function parse()
425431
* @var SelectStatement $lastStatement
426432
*/
427433
$lastStatement->union[] = $statement;
434+
435+
// if there are no no delimiting brackets, the `ORDER` and
436+
// `LIMIT` keywords actually belong to the first statement.
437+
$lastStatement->order = $statement->order;
438+
$lastStatement->limit = $statement->limit;
439+
$statement->order = null;
440+
$statement->limit = null;
441+
442+
// The statement actually ends where the last statement in
443+
// union ends.
444+
$lastStatement->last = $statement->last;
445+
428446
$inUnion = false;
429447
continue;
430448
}

src/Statement.php

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,6 @@ public function build()
122122
*/
123123
$type = $clause[1];
124124

125-
// Checking if there is any parser (builder) for this clause.
126-
if (empty(Parser::$KEYWORD_PARSERS[$name])) {
127-
continue;
128-
}
129-
130125
/**
131126
* The builder (parser) of this clause.
132127
* @var string $class

src/Statements/SelectStatement.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ class SelectStatement extends Statement
9999
'PROCEDURE' => array('PROCEDURE', 3),
100100
'INTO' => array('INTO', 3),
101101
'UNION' => array('UNION', 3),
102+
// These are available only when `UNION` is present.
103+
// 'ORDER BY' => array('ORDER BY', 3),
104+
// 'LIMIT' => array('LIMIT', 3),
102105
);
103106

104107
/**

src/Utils/Query.php

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,14 @@ public static function getClause($statement, $list, $clause, $type = 0, $skipFir
533533
*/
534534
$clauses = array_flip(array_keys($statement::$CLAUSES));
535535

536+
// This is a cheap fix for `SELECT` statements that contain `UNION`.
537+
// Replacing the `ORDER BY` or `LIMIT` clauses should replace the last
538+
// clause.
539+
if (!empty($statement->union)) {
540+
$clauses['ORDER BY'] = count($clauses) + 1;
541+
$clauses['LIMIT'] = count($clauses) + 2;
542+
}
543+
536544
/**
537545
* Lexer used for lexing the clause.
538546
* @var Lexer $lexer
@@ -552,10 +560,9 @@ public static function getClause($statement, $list, $clause, $type = 0, $skipFir
552560
$clauseIdx = $clauses[$clauseType];
553561

554562
$firstClauseIdx = $clauseIdx;
555-
556563
$lastClauseIdx = $clauseIdx + 1;
557564

558-
// Determining the behaviour of this function.
565+
// Determining the behavior of this function.
559566
if ($type === -1) {
560567
$firstClauseIdx = -1; // Something small enough.
561568
$lastClauseIdx = $clauseIdx - 1;

tests/Utils/QueryTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -385,6 +385,21 @@ public function testReplaceClause()
385385
'WHERE film_id > 0'
386386
)
387387
);
388+
389+
$parser = new Parser(
390+
'select supplier.city, supplier.id from supplier '
391+
. 'union select customer.city, customer.id from customer'
392+
);
393+
$this->assertEquals(
394+
'select supplier.city, supplier.id from supplier '
395+
. 'union select customer.city, customer.id from customer'
396+
. ' ORDER BY city ',
397+
Query::replaceClause(
398+
$parser->statements[0],
399+
$parser->list,
400+
'ORDER BY city'
401+
)
402+
);
388403
}
389404

390405
public function testReplaceClauseOnlyKeyword()

0 commit comments

Comments
 (0)