Skip to content

Commit b4e8522

Browse files
authored
Merge pull request #234 from dbould/fix-failing-tests
Fixed 3/7 failing tests
2 parents 41935eb + 4cf0cc9 commit b4e8522

13 files changed

Lines changed: 129 additions & 36 deletions

src/PHPSQLParser/processors/BracketProcessor.php

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,12 @@ protected function processTopLevel($sql) {
5757
}
5858

5959
public function process($tokens) {
60-
6160
$token = $this->removeParenthesisFromStart($tokens[0]);
6261
$subtree = $this->processTopLevel($token);
6362

63+
$remainingExpressions = $this->getRemainingNotBracketExpression($subtree);
64+
6465
if (isset($subtree['BRACKET'])) {
65-
// TODO: here we lose some other parts of a statement like ORDER BY
6666
$subtree = $subtree['BRACKET'];
6767
}
6868

@@ -73,7 +73,22 @@ public function process($tokens) {
7373

7474
return array(
7575
array('expr_type' => ExpressionType::BRACKET_EXPRESSION, 'base_expr' => trim($tokens[0]),
76-
'sub_tree' => $subtree));
76+
'sub_tree' => $subtree, 'remaining_expressions' => $remainingExpressions));
77+
}
78+
79+
private function getRemainingNotBracketExpression($subtree)
80+
{
81+
$remainingExpressions = array();
82+
$ignoredKeys = array('BRACKET', 'SELECT', 'FROM');
83+
$subtreeKeys = array_keys($subtree);
84+
85+
foreach($subtreeKeys as $key) {
86+
if(!in_array($key, $ignoredKeys)) {
87+
$remainingExpressions[$key] = $subtree[$key];
88+
}
89+
}
90+
91+
return $remainingExpressions;
7792
}
7893

7994
}

src/PHPSQLParser/processors/SQLChunkProcessor.php

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,18 @@ public function process($out) {
6666
// TODO: this field should be a global STATEMENT field within the output
6767
// we could add all other categories as sub_tree, it could also work with multipe UNIONs
6868
$processor = new BracketProcessor($this->options);
69-
$out['BRACKET'] = $processor->process($out['BRACKET']);
69+
$processedBracket = $processor->process($out['BRACKET']);
70+
$remainingExpressions = $processedBracket[0]['remaining_expressions'];
71+
72+
unset($processedBracket[0]['remaining_expressions']);
73+
74+
if(!empty($remainingExpressions)) {
75+
foreach($remainingExpressions as $key=>$expression) {
76+
$processedBracket[][$key] = $expression;
77+
}
78+
}
79+
80+
$out['BRACKET'] = $processedBracket;
7081
}
7182
if (!empty($out['CREATE'])) {
7283
$processor = new CreateProcessor($this->options);
@@ -180,6 +191,7 @@ public function process($out) {
180191
$processor = new WithProcessor($this->options);
181192
$out['WITH'] = $processor->process($out['WITH']);
182193
}
194+
183195
return $out;
184196
}
185197
}

src/PHPSQLParser/processors/UnionProcessor.php

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,16 +100,58 @@ protected function processMySQLUnion($queries) {
100100
$queries[$unionType][$key] = $this->processDefault($this->removeParenthesisFromStart($token));
101101
break;
102102
}
103-
104103
$queries[$unionType][$key] = $this->processSQL($queries[$unionType][$key]);
105104
break;
106105
}
107106
}
108107
}
108+
109109
// it can be parsed or not
110110
return $queries;
111111
}
112112

113+
/**
114+
* Moves the final union query into a separate output, so the remainder (such as ORDER BY) can
115+
* be processed separately.
116+
*/
117+
protected function splitUnionRemainder($queries, $unionType, $outputArray)
118+
{
119+
$finalQuery = [];
120+
121+
//If this token contains a matching pair of brackets at the start and end, use it as the final query
122+
foreach ($outputArray as $key => $token) {
123+
$tokenAsArray = str_split(trim($token));
124+
$keyCount = max(array_keys($tokenAsArray));
125+
126+
if (($tokenAsArray[0] == '(' && $tokenAsArray[$keyCount] == ')')) {
127+
$queries[$unionType][] = $outputArray;
128+
unset($outputArray[$key]);
129+
break;
130+
} elseif (strtoupper($token) == 'ORDER') {
131+
break;
132+
} else {
133+
$finalQuery[] = $token;
134+
unset($outputArray[$key]);
135+
}
136+
}
137+
138+
$finalQueryString = trim(implode($finalQuery));
139+
140+
if (!empty($finalQuery) && $finalQueryString != '') {
141+
$queries[$unionType][] = $finalQuery;
142+
}
143+
144+
$defaultProcessor = new DefaultProcessor($this->options);
145+
$rePrepareSqlString = trim(implode($outputArray));
146+
147+
if (!empty($rePrepareSqlString)) {
148+
$remainingQueries = $defaultProcessor->process($rePrepareSqlString);
149+
$queries[] = $remainingQueries;
150+
}
151+
152+
return $queries;
153+
}
154+
113155
public function process($inputArray) {
114156
$outputArray = array();
115157

@@ -169,14 +211,13 @@ public function process($inputArray) {
169211
// or we don't have an UNION/UNION ALL
170212
if (!empty($outputArray)) {
171213
if ($unionType) {
172-
$queries[$unionType][] = $outputArray;
214+
$queries = $this->splitUnionRemainder($queries, $unionType, $outputArray);
173215
} else {
174216
$queries[] = $outputArray;
175217
}
176218
}
177219

178220
return $this->processMySQLUnion($queries);
179221
}
180-
181222
}
182223
?>

tests/bootstrap.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,3 @@ function getExpectedValue($path, $filename, $unserialize = true) {
1414
$content = file_get_contents(dirname(__FILE__) . "/expected/" . array_pop($path) . "/" . $filename);
1515
return ($unserialize ? unserialize($content) : $content);
1616
}
17-
?>

tests/cases/parser/commentsTest.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ public function testComments2() {
3232
b
3333
FROM test';
3434
$p = $this->parser->parse($sql);
35-
$expected = getExpectedValue(dirname(__FILE__), 'comment2.serialized');
35+
$expectedEncoded = getExpectedValue(dirname(__FILE__), 'comment2.serialized', false);
36+
$expectedSerialized = base64_decode($expectedEncoded);
37+
$expected = unserialize($expectedSerialized);
38+
3639
$this->assertEquals($expected, $p, 'multi line comment');
3740
}
3841

tests/cases/parser/issue117Test.php

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,24 @@
3939
*
4040
*/
4141
namespace PHPSQLParser\Test\Parser;
42+
4243
use PHPSQLParser\PHPSQLParser;
43-
use PHPSQLParser\PHPSQLCreator;
4444
use Analog\Analog;
45+
use PHPUnit_Framework_TestCase;
4546

46-
class Issue117Test extends \PHPUnit_Framework_TestCase {
47-
48-
public function testIssue117() {
49-
47+
class Issue117Test extends PHPUnit_Framework_TestCase
48+
{
49+
public function testIssue117()
50+
{
5051
// TODO: not solved, ORDER BY has been lost
5152
$sql = "(((SELECT x FROM table)) ORDER BY x)";
53+
//$sql = "(SELECT x FROM table) ORDER BY x";
5254
$parser = new PHPSQLParser($sql, true);
5355
$p = $parser->parsed;
54-
Analog::log(print_r($p, true));
56+
5557
$expected = getExpectedValue(dirname(__FILE__), 'issue117.serialized');
56-
$this->assertEquals($expected, $p, 'parentheses on the first position of statement');
5758

59+
$this->assertEquals($expected, $p, 'parentheses on the first position of statement');
5860
}
5961
}
6062

tests/cases/parser/issue95Test.php

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,15 +39,14 @@
3939
*
4040
*/
4141
namespace PHPSQLParser\Test\Parser;
42-
use PHPSQLParser\PHPSQLParser;
43-
use PHPSQLParser\PHPSQLCreator;
44-
45-
class issue95Test extends \PHPUnit_Framework_TestCase {
46-
47-
public function testIssue95() {
4842

43+
use PHPUnit_Framework_TestCase;
44+
use PHPSQLParser\PHPSQLParser;
4945

50-
// TODO: not solved, the parser doesn't recognize the UNION
46+
class issue95Test extends PHPUnit_Framework_TestCase
47+
{
48+
public function testIssue95()
49+
{
5150
$sql="SELECT * FROM ((SELECT 1 AS `ID`) UNION (SELECT 2 AS `ID`)) AS `Tmp`";
5251

5352
try {
@@ -56,8 +55,8 @@ public function testIssue95() {
5655

5756
$p = $parser->parsed;
5857
$expected = getExpectedValue(dirname(__FILE__), 'issue95.serialized');
59-
$this->assertEquals($expected, $p, 'union within the from clause');
6058

59+
$this->assertEquals($expected, $p, 'union within the from clause');
6160
}
6261
}
6362

tests/cases/parser/unionTest.php

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,15 @@
3939
*
4040
*/
4141
namespace PHPSQLParser\Test\Parser;
42+
use PHPUnit_Framework_TestCase;
4243
use PHPSQLParser\PHPSQLParser;
4344
use PHPSQLParser\PHPSQLCreator;
4445
use Analog\Analog;
4546

46-
class UnionTest extends \PHPUnit_Framework_TestCase {
47-
public function testUnion1() {
47+
class UnionTest extends PHPUnit_Framework_TestCase
48+
{
49+
public function testUnion1()
50+
{
4851
$parser = new PHPSQLParser();
4952

5053
$sql = 'SELECT colA From test a
@@ -57,22 +60,42 @@ public function testUnion1() {
5760
$this->assertEquals($expected, $p, 'simple union');
5861
}
5962

60-
public function testUnion2() {
61-
// TODO: the order-by clause has not been parsed
63+
public function testUnion2()
64+
{
6265
$parser = new PHPSQLParser();
63-
$sql = '(SELECT colA From test a)
66+
$sql = '(SELECT colA From test a)
6467
union all
6568
(SELECT colB from test b) order by 1';
6669
$p = $parser->parse($sql, true);
6770
$expected = getExpectedValue(dirname(__FILE__), 'union2.serialized');
71+
6872
$this->assertEquals($expected, $p, 'mysql union with order-by');
6973
}
70-
public function testUnion3() {
74+
75+
public function testUnion3()
76+
{
7177
$sql = "SELECT x FROM ((SELECT y FROM z WHERE (y > 2) ) UNION ALL (SELECT a FROM z WHERE (y < 2))) as f ";
7278
$parser = new PHPSQLParser();
7379
$p = $parser->parse($sql, true);
7480
$expected = getExpectedValue(dirname(__FILE__), 'union3.serialized');
7581
$this->assertEquals($expected, $p, 'complicated mysql union');
7682
}
83+
84+
public function testUnion4()
85+
{
86+
$parser = new PHPSQLParser();
87+
88+
$sql = 'SELECT colA From test a
89+
union
90+
SELECT colB from test
91+
as b order by 1';
92+
93+
$p = $parser->parse($sql, true);
94+
Analog::log(serialize($p));
95+
$expectedSerialized = getExpectedValue(dirname(__FILE__), 'union4.serialized', false);
96+
$expected = unserialize(base64_decode($expectedSerialized));
97+
98+
$this->assertEquals($expected, $p, 'simple union with order by and no brackets');
99+
}
77100
}
78101
?>
Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1 @@
1-
a:2:{s:6:"SELECT";a:3:{i:0;a:6:{s:9:"expr_type";s:6:"colref";s:5:"alias";b:0;s:9:"base_expr";s:1:"a";s:9:"no_quotes";a:2:{s:5:"delim";b:0;s:5:"parts";a:1:{i:0;s:1:"a";}}s:8:"sub_tree";b:0;s:5:"delim";s:1:",";}i:1;a:2:{s:9:"expr_type";s:7:"comment";s:5:"value";s:109:"/*
2-
multi line
3-
comment
4-
*/";}i:2;a:6:{s:9:"expr_type";s:6:"colref";s:5:"alias";b:0;s:9:"base_expr";s:1:"b";s:9:"no_quotes";a:2:{s:5:"delim";b:0;s:5:"parts";a:1:{i:0;s:1:"b";}}s:8:"sub_tree";b:0;s:5:"delim";b:0;}}s:4:"FROM";a:1:{i:0;a:10:{s:9:"expr_type";s:5:"table";s:5:"table";s:4:"test";s:9:"no_quotes";a:2:{s:5:"delim";b:0;s:5:"parts";a:1:{i:0;s:4:"test";}}s:5:"alias";b:0;s:5:"hints";b:0;s:9:"join_type";s:4:"JOIN";s:8:"ref_type";b:0;s:10:"ref_clause";b:0;s:9:"base_expr";s:4:"test";s:8:"sub_tree";b:0;}}}
1+
YToyOntzOjY6IlNFTEVDVCI7YTozOntpOjA7YTo2OntzOjk6ImV4cHJfdHlwZSI7czo2OiJjb2xyZWYiO3M6NToiYWxpYXMiO2I6MDtzOjk6ImJhc2VfZXhwciI7czoxOiJhIjtzOjk6Im5vX3F1b3RlcyI7YToyOntzOjU6ImRlbGltIjtiOjA7czo1OiJwYXJ0cyI7YToxOntpOjA7czoxOiJhIjt9fXM6ODoic3ViX3RyZWUiO2I6MDtzOjU6ImRlbGltIjtzOjE6IiwiO31pOjE7YToyOntzOjk6ImV4cHJfdHlwZSI7czo3OiJjb21tZW50IjtzOjU6InZhbHVlIjtzOjEwNjoiLyogCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBtdWx0aSBsaW5lIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgY29tbWVudAogICAgICAgICAgICAgICAgICAgICAgICAqLyI7fWk6MjthOjY6e3M6OToiZXhwcl90eXBlIjtzOjY6ImNvbHJlZiI7czo1OiJhbGlhcyI7YjowO3M6OToiYmFzZV9leHByIjtzOjE6ImIiO3M6OToibm9fcXVvdGVzIjthOjI6e3M6NToiZGVsaW0iO2I6MDtzOjU6InBhcnRzIjthOjE6e2k6MDtzOjE6ImIiO319czo4OiJzdWJfdHJlZSI7YjowO3M6NToiZGVsaW0iO2I6MDt9fXM6NDoiRlJPTSI7YToxOntpOjA7YToxMDp7czo5OiJleHByX3R5cGUiO3M6NToidGFibGUiO3M6NToidGFibGUiO3M6NDoidGVzdCI7czo5OiJub19xdW90ZXMiO2E6Mjp7czo1OiJkZWxpbSI7YjowO3M6NToicGFydHMiO2E6MTp7aTowO3M6NDoidGVzdCI7fX1zOjU6ImFsaWFzIjtiOjA7czo1OiJoaW50cyI7YjowO3M6OToiam9pbl90eXBlIjtzOjQ6IkpPSU4iO3M6ODoicmVmX3R5cGUiO2I6MDtzOjEwOiJyZWZfY2xhdXNlIjtiOjA7czo5OiJiYXNlX2V4cHIiO3M6NDoidGVzdCI7czo4OiJzdWJfdHJlZSI7YjowO319fQ==
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
a:1:{s:7:"BRACKET";a:1:{i:0;a:4:{s:9:"expr_type";s:18:"bracket_expression";s:9:"base_expr";s:36:"(((SELECT x FROM table)) ORDER BY x)";s:8:"sub_tree";a:1:{i:0;a:4:{s:9:"expr_type";s:18:"bracket_expression";s:9:"base_expr";s:23:"((SELECT x FROM table))";s:8:"sub_tree";a:1:{i:0;a:4:{s:9:"expr_type";s:18:"bracket_expression";s:9:"base_expr";s:21:"(SELECT x FROM table)";s:8:"sub_tree";a:1:{i:0;a:4:{s:9:"expr_type";s:5:"query";s:9:"base_expr";s:19:"SELECT x FROM table";s:8:"sub_tree";a:2:{s:6:"SELECT";a:1:{i:0;a:7:{s:9:"expr_type";s:6:"colref";s:5:"alias";b:0;s:9:"base_expr";s:1:"x";s:9:"no_quotes";s:1:"x";s:8:"sub_tree";b:0;s:5:"delim";b:0;s:8:"position";i:10;}}s:4:"FROM";a:1:{i:0;a:10:{s:9:"expr_type";s:5:"table";s:5:"table";s:5:"table";s:9:"no_quotes";s:5:"table";s:5:"alias";b:0;s:9:"join_type";s:4:"JOIN";s:8:"ref_type";b:0;s:10:"ref_clause";b:0;s:9:"base_expr";s:5:"table";s:8:"sub_tree";b:0;s:8:"position";i:17;}}}s:8:"position";i:3;}}s:8:"position";i:2;}}s:8:"position";i:1;}}s:8:"position";i:0;}}}
1+
a:1:{s:7:"BRACKET";a:2:{i:0;a:4:{s:9:"expr_type";s:18:"bracket_expression";s:9:"base_expr";s:36:"(((SELECT x FROM table)) ORDER BY x)";s:8:"sub_tree";a:1:{i:0;a:4:{s:9:"expr_type";s:18:"bracket_expression";s:9:"base_expr";s:23:"((SELECT x FROM table))";s:8:"sub_tree";a:1:{i:0;a:4:{s:9:"expr_type";s:18:"bracket_expression";s:9:"base_expr";s:21:"(SELECT x FROM table)";s:8:"sub_tree";a:1:{i:0;a:4:{s:9:"expr_type";s:5:"query";s:9:"base_expr";s:19:"SELECT x FROM table";s:8:"sub_tree";a:2:{s:6:"SELECT";a:1:{i:0;a:7:{s:9:"expr_type";s:6:"colref";s:5:"alias";b:0;s:9:"base_expr";s:1:"x";s:9:"no_quotes";a:2:{s:5:"delim";b:0;s:5:"parts";a:1:{i:0;s:1:"x";}}s:8:"sub_tree";b:0;s:5:"delim";b:0;s:8:"position";i:10;}}s:4:"FROM";a:1:{i:0;a:11:{s:9:"expr_type";s:5:"table";s:5:"table";s:5:"table";s:9:"no_quotes";a:2:{s:5:"delim";b:0;s:5:"parts";a:1:{i:0;s:5:"table";}}s:5:"alias";b:0;s:5:"hints";b:0;s:9:"join_type";s:4:"JOIN";s:8:"ref_type";b:0;s:10:"ref_clause";b:0;s:9:"base_expr";s:5:"table";s:8:"sub_tree";b:0;s:8:"position";i:17;}}}s:8:"position";i:3;}}s:8:"position";i:2;}}s:8:"position";i:1;}}s:8:"position";i:0;}i:1;a:1:{s:5:"ORDER";a:1:{i:0;a:6:{s:9:"expr_type";s:6:"colref";s:9:"base_expr";s:1:"x";s:9:"no_quotes";a:2:{s:5:"delim";b:0;s:5:"parts";a:1:{i:0;s:1:"x";}}s:8:"sub_tree";b:0;s:9:"direction";s:3:"ASC";s:8:"position";i:34;}}}}}

0 commit comments

Comments
 (0)