Skip to content

Commit 991fe54

Browse files
committed
Fix the order of clauses in SELECT statements involing UNIONs.
1 parent 03eb61c commit 991fe54

File tree

4 files changed

+61
-11
lines changed

4 files changed

+61
-11
lines changed

src/Parser.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ class Parser
151151
'FROM' => array(
152152
'class' => 'SqlParser\\Components\\ExpressionArray',
153153
'field' => 'from',
154-
'options' => array('skipColumn' => true),
154+
'options' => array('skipColumn' => true, 'noBrackets' => true),
155155
),
156156
'GROUP BY' => array(
157157
'class' => 'SqlParser\\Components\\OrderKeyword',
@@ -297,6 +297,13 @@ class Parser
297297
*/
298298
public $statements = array();
299299

300+
/**
301+
* The number of opened brackets.
302+
*
303+
* @var int
304+
*/
305+
public $brackets = 0;
306+
300307
/**
301308
* Constructor.
302309
*
@@ -381,6 +388,12 @@ public function parse()
381388
continue;
382389
}
383390

391+
// Counting the brackets around statements.
392+
if ($token->value === '(') {
393+
++$this->brackets;
394+
continue;
395+
}
396+
384397
// Statements can start with keywords only.
385398
// Comments, whitespaces, etc. are ignored.
386399
if ($token->type !== Token::TYPE_KEYWORD) {

src/Statement.php

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,14 @@ public function build()
122122
*/
123123
$built = array();
124124

125-
foreach (static::$CLAUSES as $clause) {
125+
/**
126+
* Statement's clauses.
127+
*
128+
* @var array
129+
*/
130+
$clauses = $this->getClauses();
131+
132+
foreach ($clauses as $clause) {
126133
/**
127134
* The name of the clause.
128135
*
@@ -224,6 +231,13 @@ public function parse(Parser $parser, TokensList $list)
224231
break;
225232
}
226233

234+
// Checking if this closing bracket is the pair for a bracket
235+
// outside the statement.
236+
if (($token->value === ')') && ($parser->brackets > 0)) {
237+
--$parser->brackets;
238+
continue;
239+
}
240+
227241
// Only keywords are relevant here. Other parts of the query are
228242
// processed in the functions below.
229243
if ($token->type !== Token::TYPE_KEYWORD) {
@@ -363,6 +377,16 @@ public function after(Parser $parser, TokensList $list, Token $token)
363377

364378
}
365379

380+
/**
381+
* Gets the clauses of this statement.
382+
*
383+
* @return array
384+
*/
385+
public function getClauses()
386+
{
387+
return static::$CLAUSES;
388+
}
389+
366390
/**
367391
* Builds the string representation of this statement.
368392
*

src/Statements/SelectStatement.php

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,25 @@ class SelectStatement extends Statement
195195
* @var SelectStatement[]
196196
*/
197197
public $union = array();
198+
199+
/**
200+
* Gets the clauses of this statement.
201+
*
202+
* @return array
203+
*/
204+
public function getClauses()
205+
{
206+
// This is a cheap fix for `SELECT` statements that contain `UNION`.
207+
// The `ORDER BY` and `LIMIT` clauses should be at the end of the
208+
// statement.
209+
if (!empty($this->union)) {
210+
$clauses = static::$CLAUSES;
211+
unset($clauses['ORDER BY']);
212+
unset($clauses['LIMIT']);
213+
$clauses['ORDER BY'] = array('ORDER BY', 3);
214+
$clauses['LIMIT'] = array('LIMIT', 3);
215+
return $clauses;
216+
}
217+
return static::$CLAUSES;
218+
}
198219
}

src/Utils/Query.php

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -546,15 +546,7 @@ public static function getClause($statement, $list, $clause, $type = 0, $skipFir
546546
*
547547
* @var array $clauses
548548
*/
549-
$clauses = array_flip(array_keys($statement::$CLAUSES));
550-
551-
// This is a cheap fix for `SELECT` statements that contain `UNION`.
552-
// Replacing the `ORDER BY` or `LIMIT` clauses should replace the last
553-
// clause.
554-
if (($statement instanceof SelectStatement) && (!empty($statement->union))) {
555-
$clauses['ORDER BY'] = count($clauses) + 1;
556-
$clauses['LIMIT'] = count($clauses) + 2;
557-
}
549+
$clauses = array_flip(array_keys($statement->getClauses()));
558550

559551
/**
560552
* Lexer used for lexing the clause.

0 commit comments

Comments
 (0)