summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--src/Statement.php36
-rw-r--r--src/Utils/Formatter.php10
-rw-r--r--tests/Parser/SelectStatementTest.php1
-rw-r--r--tests/Utils/FormatterTest.php7
-rw-r--r--tests/data/parser/parseSelectWrongOrder2.in1
-rw-r--r--tests/data/parser/parseSelectWrongOrder2.out1
7 files changed, 47 insertions, 11 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5080711..45593fd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,8 @@
* Coding style improvements.
* Chinese localization.
+* Improved order validatin for JOIN clauses.
+* Improved pretty printing of JOIN clauses.
## [4.1.1] - 2017-02-07
diff --git a/src/Statement.php b/src/Statement.php
index dc5f945..50b1285 100644
--- a/src/Statement.php
+++ b/src/Statement.php
@@ -451,16 +451,26 @@ abstract class Statement
/**
* For tracking JOIN clauses in a query
- * 0 - JOIN not found till now
- * 1 - JOIN has been found
- * 2 - A Non-JOIN clause has been found
- * after a previously found JOIN clause.
+ * = 0 - JOIN not found till now
+ * > 0 - Index of first JOIN clause in the statement
*
* @var int
*/
- $joinStart = 0;
+ $minJoin = 0;
+
+ /**
+ * For tracking JOIN clauses in a query
+ * = 0 - JOIN not found till now
+ * > 0 - Index of last JOIN clause
+ * (which appears together with other JOINs)
+ * in the statement
+ *
+ * @var int
+ */
+ $maxJoin = 0;
$error = 0;
+ $lastIdx = 0;
foreach ($clauses as $clauseType => $index) {
$clauseStartIdx = Utils\Query::getClauseStartOffset(
$this,
@@ -470,17 +480,19 @@ abstract class Statement
// Handle ordering of Multiple Joins in a query
if ($clauseStartIdx != -1) {
- if ($joinStart == 0 && stripos($clauseType, 'JOIN') !== false) {
- $joinStart = 1;
- } elseif ($joinStart == 1 && stripos($clauseType, 'JOIN') === false) {
- $joinStart = 2;
- } elseif ($joinStart == 2 && stripos($clauseType, 'JOIN') !== false) {
+ if ($minJoin === 0 && stripos($clauseType, 'JOIN')) {
+ // First JOIN clause is detected
+ $minJoin = $maxJoin = $clauseStartIdx;
+ } elseif ($minJoin !== 0 && ! stripos($clauseType, 'JOIN')) {
+ // After a previous JOIN clause, a non-JOIN clause has been detected
+ $maxJoin = $lastIdx;
+ } elseif ($maxJoin < $clauseStartIdx && stripos($clauseType, 'JOIN')) {
$error = 1;
}
}
if ($clauseStartIdx != -1 && $clauseStartIdx < $minIdx) {
- if ($joinStart == 0 || ($joinStart == 2 && $error = 1)) {
+ if ($minJoin === 0 || $error === 1) {
$token = $list->tokens[$clauseStartIdx];
$parser->error(
'Unexpected ordering of clauses.',
@@ -493,6 +505,8 @@ abstract class Statement
} elseif ($clauseStartIdx != -1) {
$minIdx = $clauseStartIdx;
}
+
+ $lastIdx = ($clauseStartIdx !== -1) ? $clauseStartIdx : $lastIdx;
}
return true;
diff --git a/src/Utils/Formatter.php b/src/Utils/Formatter.php
index 16c0778..e07394c 100644
--- a/src/Utils/Formatter.php
+++ b/src/Utils/Formatter.php
@@ -6,6 +6,7 @@
namespace PhpMyAdmin\SqlParser\Utils;
+use PhpMyAdmin\SqlParser\Components\JoinKeyword;
use PhpMyAdmin\SqlParser\Lexer;
use PhpMyAdmin\SqlParser\Parser;
use PhpMyAdmin\SqlParser\Token;
@@ -383,6 +384,15 @@ class Formatter
}
}
+ // Inline JOINs
+ if (($prev->type === Token::TYPE_KEYWORD && isset(JoinKeyword::$JOINS[$prev->value]))
+ || (in_array($curr->value, array('ON', 'USING'), true) && isset(JoinKeyword::$JOINS[$list->tokens[$list->idx - 2]->value]))
+ || (isset($list->tokens[$list->idx - 4]) && isset(JoinKeyword::$JOINS[$list->tokens[$list->idx - 4]->value]))
+ || (isset($list->tokens[$list->idx - 6]) && isset(JoinKeyword::$JOINS[$list->tokens[$list->idx - 6]->value]))
+ ) {
+ $lineEnded = false;
+ }
+
// Indenting BEGIN ... END blocks.
if ($prev->type === Token::TYPE_KEYWORD && $prev->keyword === 'BEGIN') {
$lineEnded = true;
diff --git a/tests/Parser/SelectStatementTest.php b/tests/Parser/SelectStatementTest.php
index 5be345f..42fb955 100644
--- a/tests/Parser/SelectStatementTest.php
+++ b/tests/Parser/SelectStatementTest.php
@@ -63,6 +63,7 @@ class SelectStatementTest extends TestCase
array('parser/parseSelectJoinMultiple'),
array('parser/parseSelectJoinMultiple2'),
array('parser/parseSelectWrongOrder'),
+ array('parser/parseSelectWrongOrder2'),
array('parser/parseSelectEndOptions1'),
array('parser/parseSelectEndOptions2'),
array('parser/parseSelectEndOptionsErr'),
diff --git a/tests/Utils/FormatterTest.php b/tests/Utils/FormatterTest.php
index b8a8ff2..c209d27 100644
--- a/tests/Utils/FormatterTest.php
+++ b/tests/Utils/FormatterTest.php
@@ -508,6 +508,13 @@ class FormatTest extends TestCase
'&nbsp;&nbsp;&nbsp;&nbsp;<span class="sql-variable">`query`</span> <span class="sql-keyword">TEXT</span> <span class="sql-reserved">NOT NULL</span>,' . '<br/>' .
'&nbsp;&nbsp;&nbsp;&nbsp;<span class="sql-reserved">PRIMARY KEY</span>(<span class="sql-variable">`id`</span>)',
),
+ 'join' => array(
+ 'query' => 'join tbl2 on c1=c2',
+ 'text' => 'JOIN tbl2 ON c1 = c2',
+ 'cli' => "\x1b[35mJOIN \x1b[39mtbl2 \x1b[35mON \x1b[39mc1 \x1b[39m= \x1b[39mc2" .
+ "\x1b[0m",
+ 'html' => '<span class="sql-reserved">JOIN</span> tbl2 <span class="sql-reserved">ON</span> c1 = c2',
+ ),
);
}
}
diff --git a/tests/data/parser/parseSelectWrongOrder2.in b/tests/data/parser/parseSelectWrongOrder2.in
new file mode 100644
index 0000000..3a000ec
--- /dev/null
+++ b/tests/data/parser/parseSelectWrongOrder2.in
@@ -0,0 +1 @@
+SELECT DISTINCT * FROM tbl1 INNER JOIN tbl2 ON id1 = id2 WHERE 1=1 LEFT OUTER JOIN l3; \ No newline at end of file
diff --git a/tests/data/parser/parseSelectWrongOrder2.out b/tests/data/parser/parseSelectWrongOrder2.out
new file mode 100644
index 0000000..b83f6fb
--- /dev/null
+++ b/tests/data/parser/parseSelectWrongOrder2.out
@@ -0,0 +1 @@
+a:4:{s:5:"query";s:86:"SELECT DISTINCT * FROM tbl1 INNER JOIN tbl2 ON id1 = id2 WHERE 1=1 LEFT OUTER JOIN l3;";s:5:"lexer";O:15:"SqlParser\Lexer":8:{s:6:"strict";b:0;s:3:"str";s:86:"SELECT DISTINCT * FROM tbl1 INNER JOIN tbl2 ON id1 = id2 WHERE 1=1 LEFT OUTER JOIN l3;";s:3:"len";i:86;s:4:"last";i:86;s:4:"list";O:20:"SqlParser\TokensList":3:{s:6:"tokens";a:33:{i:0;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"SELECT";s:5:"value";s:6:"SELECT";s:4:"type";i:1;s:5:"flags";i:3;s:8:"position";i:0;}i:1;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:6;}i:2;O:15:"SqlParser\Token":5:{s:5:"token";s:8:"DISTINCT";s:5:"value";s:8:"DISTINCT";s:4:"type";i:1;s:5:"flags";i:3;s:8:"position";i:7;}i:3;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:15;}i:4;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"*";s:5:"value";s:1:"*";s:4:"type";i:2;s:5:"flags";i:1;s:8:"position";i:16;}i:5;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:17;}i:6;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"FROM";s:5:"value";s:4:"FROM";s:4:"type";i:1;s:5:"flags";i:3;s:8:"position";i:18;}i:7;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:22;}i:8;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"tbl1";s:5:"value";s:4:"tbl1";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:23;}i:9;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:27;}i:10;O:15:"SqlParser\Token":5:{s:5:"token";s:10:"INNER JOIN";s:5:"value";s:10:"INNER JOIN";s:4:"type";i:1;s:5:"flags";i:7;s:8:"position";i:28;}i:11;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:38;}i:12;O:15:"SqlParser\Token":5:{s:5:"token";s:4:"tbl2";s:5:"value";s:4:"tbl2";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:39;}i:13;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:43;}i:14;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"ON";s:5:"value";s:2:"ON";s:4:"type";i:1;s:5:"flags";i:3;s:8:"position";i:44;}i:15;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:46;}i:16;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"id1";s:5:"value";s:3:"id1";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:47;}i:17;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:50;}i:18;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"=";s:5:"value";s:1:"=";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:51;}i:19;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:52;}i:20;O:15:"SqlParser\Token":5:{s:5:"token";s:3:"id2";s:5:"value";s:3:"id2";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:53;}i:21;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:56;}i:22;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"WHERE";s:5:"value";s:5:"WHERE";s:4:"type";i:1;s:5:"flags";i:3;s:8:"position";i:57;}i:23;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:62;}i:24;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"1";s:5:"value";i:1;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:63;}i:25;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"=";s:5:"value";s:1:"=";s:4:"type";i:2;s:5:"flags";i:2;s:8:"position";i:64;}i:26;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"1";s:5:"value";i:1;s:4:"type";i:6;s:5:"flags";i:0;s:8:"position";i:65;}i:27;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:66;}i:28;O:15:"SqlParser\Token":5:{s:5:"token";s:15:"LEFT OUTER JOIN";s:5:"value";s:15:"LEFT OUTER JOIN";s:4:"type";i:1;s:5:"flags";i:7;s:8:"position";i:67;}i:29;O:15:"SqlParser\Token":5:{s:5:"token";s:1:" ";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:82;}i:30;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"l3";s:5:"value";s:2:"l3";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:83;}i:31;O:15:"SqlParser\Token":5:{s:5:"token";s:1:";";s:5:"value";s:1:";";s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";i:85;}i:32;O:15:"SqlParser\Token":5:{s:5:"token";N;s:5:"value";N;s:4:"type";i:9;s:5:"flags";i:0;s:8:"position";N;}}s:5:"count";i:33;s:3:"idx";i:33;}s:9:"delimiter";s:1:";";s:12:"delimiterLen";i:1;s:6:"errors";a:0:{}}s:6:"parser";O:16:"SqlParser\Parser":5:{s:4:"list";r:8;s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:1:{i:0;O:36:"SqlParser\Statements\SelectStatement":16:{s:4:"expr";a:1:{i:0;O:31:"SqlParser\Components\Expression":7:{s:8:"database";N;s:5:"table";N;s:6:"column";N;s:4:"expr";s:1:"*";s:5:"alias";N;s:8:"function";N;s:8:"subquery";N;}}s:4:"from";a:1:{i:0;O:31:"SqlParser\Components\Expression":7:{s:8:"database";N;s:5:"table";s:4:"tbl1";s:6:"column";N;s:4:"expr";s:4:"tbl1";s:5:"alias";N;s:8:"function";N;s:8:"subquery";N;}}s:9:"partition";N;s:5:"where";a:1:{i:0;O:30:"SqlParser\Components\Condition":3:{s:11:"identifiers";a:0:{}s:10:"isOperator";b:0;s:4:"expr";s:3:"1=1";}}s:5:"group";N;s:6:"having";N;s:5:"order";N;s:5:"limit";N;s:9:"procedure";N;s:4:"into";N;s:4:"join";a:1:{i:0;O:32:"SqlParser\Components\JoinKeyword":4:{s:4:"type";s:4:"LEFT";s:4:"expr";O:31:"SqlParser\Components\Expression":7:{s:8:"database";N;s:5:"table";s:2:"l3";s:6:"column";N;s:4:"expr";s:2:"l3";s:5:"alias";N;s:8:"function";N;s:8:"subquery";N;}s:2:"on";N;s:5:"using";N;}}s:5:"union";a:0:{}s:11:"end_options";N;s:7:"options";O:33:"SqlParser\Components\OptionsArray":1:{s:7:"options";a:1:{i:1;s:8:"DISTINCT";}}s:5:"first";i:0;s:4:"last";i:30;}}s:8:"brackets";i:0;}s:6:"errors";a:2:{s:5:"lexer";a:0:{}s:6:"parser";a:1:{i:0;a:3:{i:0;s:31:"Unexpected ordering of clauses.";i:1;r:142;i:2;i:0;}}}} \ No newline at end of file