diff options
author | Deven Bansod <devenbansod.bits@gmail.com> | 2016-10-25 19:29:15 +0530 |
---|---|---|
committer | Deven Bansod <devenbansod.bits@gmail.com> | 2016-10-25 19:29:15 +0530 |
commit | 94e550e4a9b359332bab257a5f4aa6dfb8e48ea7 (patch) | |
tree | 333f35e531cd1f18011e95bc6f1c974adae7b096 /src | |
parent | c70914ac7e54904bb7e0ed0e3a9511481f69168d (diff) | |
parent | dccefe437ad94182a22376807950b7bf5e0b9a47 (diff) | |
download | sql-parser-94e550e4a9b359332bab257a5f4aa6dfb8e48ea7.zip sql-parser-94e550e4a9b359332bab257a5f4aa6dfb8e48ea7.tar.gz sql-parser-94e550e4a9b359332bab257a5f4aa6dfb8e48ea7.tar.bz2 |
Merge branch 'master' of https://github.com/phpmyadmin/sql-parser
Diffstat (limited to 'src')
-rw-r--r-- | src/Components/JoinKeyword.php | 20 | ||||
-rw-r--r-- | src/Context.php | 10 | ||||
-rw-r--r-- | src/Contexts/ContextMySql50000.php | 15 | ||||
-rw-r--r-- | src/Contexts/ContextMySql50100.php | 15 | ||||
-rw-r--r-- | src/Contexts/ContextMySql50500.php | 15 | ||||
-rw-r--r-- | src/Contexts/ContextMySql50600.php | 15 | ||||
-rw-r--r-- | src/Contexts/ContextMySql50700.php | 15 | ||||
-rw-r--r-- | src/Lexer.php | 57 | ||||
-rw-r--r-- | src/Parser.php | 22 | ||||
-rw-r--r-- | src/Statements/SelectStatement.php | 49 | ||||
-rw-r--r-- | src/Token.php | 12 |
11 files changed, 184 insertions, 61 deletions
diff --git a/src/Components/JoinKeyword.php b/src/Components/JoinKeyword.php index 7062783..548a64e 100644 --- a/src/Components/JoinKeyword.php +++ b/src/Components/JoinKeyword.php @@ -30,6 +30,7 @@ class JoinKeyword extends Component * @var array */ public static $JOINS = array( + 'CROSS JOIN' => 'CROSS', 'FULL JOIN' => 'FULL', 'FULL OUTER JOIN' => 'FULL', 'INNER JOIN' => 'INNER', @@ -38,6 +39,12 @@ class JoinKeyword extends Component 'LEFT OUTER JOIN' => 'LEFT', 'RIGHT JOIN' => 'RIGHT', 'RIGHT OUTER JOIN' => 'RIGHT', + 'NATURAL JOIN' => 'NATURAL', + 'NATURAL LEFT JOIN' => 'NATURAL LEFT', + 'NATURAL LEFT JOIN' => 'NATURAL LEFT', + 'NATURAL RIGHT JOIN' => 'NATURAL RIGHT', + 'NATURAL LEFT OUTER JOIN' => 'NATURAL LEFT OUTER', + 'NATURAL RIGHT OUTER JOIN' => 'NATURAL RIGHT OUTER', 'STRAIGHT_JOIN' => 'STRAIGHT', ); @@ -147,8 +154,17 @@ class JoinKeyword extends Component } elseif ($token->value === 'USING') { $state = 4; } else { - /* Next clause is starting */ - break; + if (($token->type === Token::TYPE_KEYWORD) + && (!empty(static::$JOINS[$token->value])) + ) { + $ret[] = $expr; + $expr = new JoinKeyword(); + $expr->type = static::$JOINS[$token->value]; + $state = 1; + } else { + /* Next clause is starting */ + break; + } } } } elseif ($state === 3) { diff --git a/src/Context.php b/src/Context.php index 0bdc696..3685857 100644 --- a/src/Context.php +++ b/src/Context.php @@ -30,6 +30,16 @@ abstract class Context const KEYWORD_MAX_LENGTH = 30; /** + * The maximum length of a label. + * + * @see static::$TOKEN_LABEL + * Ref: https://dev.mysql.com/doc/refman/5.7/en/statement-labels.html + * + * @var int + */ + const LABEL_MAX_LENGTH = 16; + + /** * The maximum length of an operator. * * @see static::$TOKEN_OPERATOR diff --git a/src/Contexts/ContextMySql50000.php b/src/Contexts/ContextMySql50000.php index 4931faa..f8afd18 100644 --- a/src/Contexts/ContextMySql50000.php +++ b/src/Contexts/ContextMySql50000.php @@ -145,20 +145,21 @@ class ContextMySql50000 extends Context 'AND CHAIN' => 7, 'FULL JOIN' => 7, 'IF EXISTS' => 7, 'LEFT JOIN' => 7, 'LESS THAN' => 7, 'NO ACTION' => 7, 'ON DELETE' => 7, 'ON UPDATE' => 7, 'UNION ALL' => 7, - 'INNER JOIN' => 7, 'LINEAR KEY' => 7, 'NO RELEASE' => 7, 'OR REPLACE' => 7, - 'RIGHT JOIN' => 7, + 'CROSS JOIN' => 7, 'INNER JOIN' => 7, 'LINEAR KEY' => 7, 'NO RELEASE' => 7, + 'OR REPLACE' => 7, 'RIGHT JOIN' => 7, 'LINEAR HASH' => 7, - 'AND NO CHAIN' => 7, 'FOR EACH ROW' => 7, 'PARTITION BY' => 7, - 'SET PASSWORD' => 7, 'SQL SECURITY' => 7, + 'AND NO CHAIN' => 7, 'FOR EACH ROW' => 7, 'NATURAL JOIN' => 7, + 'PARTITION BY' => 7, 'SET PASSWORD' => 7, 'SQL SECURITY' => 7, 'CHARACTER SET' => 7, 'IF NOT EXISTS' => 7, 'DATA DIRECTORY' => 7, 'UNION DISTINCT' => 7, 'DEFAULT CHARSET' => 7, 'DEFAULT COLLATE' => 7, 'FULL OUTER JOIN' => 7, 'INDEX DIRECTORY' => 7, 'LEFT OUTER JOIN' => 7, 'SUBPARTITION BY' => 7, 'GENERATED ALWAYS' => 7, 'RIGHT OUTER JOIN' => 7, - 'START TRANSACTION' => 7, - 'SELECT TRANSACTION' => 7, + 'NATURAL LEFT JOIN' => 7, 'START TRANSACTION' => 7, + 'NATURAL RIGHT JOIN' => 7, 'SELECT TRANSACTION' => 7, 'DEFAULT CHARACTER SET' => 7, - 'WITH CONSISTENT SNAPSHOT' => 7, + 'NATURAL LEFT OUTER JOIN' => 7, + 'NATURAL RIGHT OUTER JOIN' => 7, 'WITH CONSISTENT SNAPSHOT' => 7, 'BIT' => 9, 'XML' => 9, 'ENUM' => 9, 'JSON' => 9, 'TEXT' => 9, diff --git a/src/Contexts/ContextMySql50100.php b/src/Contexts/ContextMySql50100.php index a547e64..dd2b677 100644 --- a/src/Contexts/ContextMySql50100.php +++ b/src/Contexts/ContextMySql50100.php @@ -159,20 +159,21 @@ class ContextMySql50100 extends Context 'AND CHAIN' => 7, 'FULL JOIN' => 7, 'IF EXISTS' => 7, 'LEFT JOIN' => 7, 'LESS THAN' => 7, 'NO ACTION' => 7, 'ON DELETE' => 7, 'ON UPDATE' => 7, 'UNION ALL' => 7, - 'INNER JOIN' => 7, 'LINEAR KEY' => 7, 'NO RELEASE' => 7, 'OR REPLACE' => 7, - 'RIGHT JOIN' => 7, + 'CROSS JOIN' => 7, 'INNER JOIN' => 7, 'LINEAR KEY' => 7, 'NO RELEASE' => 7, + 'OR REPLACE' => 7, 'RIGHT JOIN' => 7, 'LINEAR HASH' => 7, - 'AND NO CHAIN' => 7, 'FOR EACH ROW' => 7, 'PARTITION BY' => 7, - 'SET PASSWORD' => 7, 'SQL SECURITY' => 7, + 'AND NO CHAIN' => 7, 'FOR EACH ROW' => 7, 'NATURAL JOIN' => 7, + 'PARTITION BY' => 7, 'SET PASSWORD' => 7, 'SQL SECURITY' => 7, 'CHARACTER SET' => 7, 'IF NOT EXISTS' => 7, 'DATA DIRECTORY' => 7, 'UNION DISTINCT' => 7, 'DEFAULT CHARSET' => 7, 'DEFAULT COLLATE' => 7, 'FULL OUTER JOIN' => 7, 'INDEX DIRECTORY' => 7, 'LEFT OUTER JOIN' => 7, 'SUBPARTITION BY' => 7, 'GENERATED ALWAYS' => 7, 'RIGHT OUTER JOIN' => 7, - 'START TRANSACTION' => 7, - 'SELECT TRANSACTION' => 7, + 'NATURAL LEFT JOIN' => 7, 'START TRANSACTION' => 7, + 'NATURAL RIGHT JOIN' => 7, 'SELECT TRANSACTION' => 7, 'DEFAULT CHARACTER SET' => 7, - 'WITH CONSISTENT SNAPSHOT' => 7, + 'NATURAL LEFT OUTER JOIN' => 7, + 'NATURAL RIGHT OUTER JOIN' => 7, 'WITH CONSISTENT SNAPSHOT' => 7, 'BIT' => 9, 'XML' => 9, 'ENUM' => 9, 'JSON' => 9, 'TEXT' => 9, diff --git a/src/Contexts/ContextMySql50500.php b/src/Contexts/ContextMySql50500.php index a15744a..02acdac 100644 --- a/src/Contexts/ContextMySql50500.php +++ b/src/Contexts/ContextMySql50500.php @@ -163,20 +163,21 @@ class ContextMySql50500 extends Context 'AND CHAIN' => 7, 'FULL JOIN' => 7, 'IF EXISTS' => 7, 'LEFT JOIN' => 7, 'LESS THAN' => 7, 'NO ACTION' => 7, 'ON DELETE' => 7, 'ON UPDATE' => 7, 'UNION ALL' => 7, - 'INNER JOIN' => 7, 'LINEAR KEY' => 7, 'NO RELEASE' => 7, 'OR REPLACE' => 7, - 'RIGHT JOIN' => 7, + 'CROSS JOIN' => 7, 'INNER JOIN' => 7, 'LINEAR KEY' => 7, 'NO RELEASE' => 7, + 'OR REPLACE' => 7, 'RIGHT JOIN' => 7, 'LINEAR HASH' => 7, - 'AND NO CHAIN' => 7, 'FOR EACH ROW' => 7, 'PARTITION BY' => 7, - 'SET PASSWORD' => 7, 'SQL SECURITY' => 7, + 'AND NO CHAIN' => 7, 'FOR EACH ROW' => 7, 'NATURAL JOIN' => 7, + 'PARTITION BY' => 7, 'SET PASSWORD' => 7, 'SQL SECURITY' => 7, 'CHARACTER SET' => 7, 'IF NOT EXISTS' => 7, 'DATA DIRECTORY' => 7, 'UNION DISTINCT' => 7, 'DEFAULT CHARSET' => 7, 'DEFAULT COLLATE' => 7, 'FULL OUTER JOIN' => 7, 'INDEX DIRECTORY' => 7, 'LEFT OUTER JOIN' => 7, 'SUBPARTITION BY' => 7, 'GENERATED ALWAYS' => 7, 'RIGHT OUTER JOIN' => 7, - 'START TRANSACTION' => 7, - 'SELECT TRANSACTION' => 7, + 'NATURAL LEFT JOIN' => 7, 'START TRANSACTION' => 7, + 'NATURAL RIGHT JOIN' => 7, 'SELECT TRANSACTION' => 7, 'DEFAULT CHARACTER SET' => 7, - 'WITH CONSISTENT SNAPSHOT' => 7, + 'NATURAL LEFT OUTER JOIN' => 7, + 'NATURAL RIGHT OUTER JOIN' => 7, 'WITH CONSISTENT SNAPSHOT' => 7, 'BIT' => 9, 'XML' => 9, 'ENUM' => 9, 'JSON' => 9, 'TEXT' => 9, diff --git a/src/Contexts/ContextMySql50600.php b/src/Contexts/ContextMySql50600.php index 7a0cf56..1143978 100644 --- a/src/Contexts/ContextMySql50600.php +++ b/src/Contexts/ContextMySql50600.php @@ -169,20 +169,21 @@ class ContextMySql50600 extends Context 'AND CHAIN' => 7, 'FULL JOIN' => 7, 'IF EXISTS' => 7, 'LEFT JOIN' => 7, 'LESS THAN' => 7, 'NO ACTION' => 7, 'ON DELETE' => 7, 'ON UPDATE' => 7, 'UNION ALL' => 7, - 'INNER JOIN' => 7, 'LINEAR KEY' => 7, 'NO RELEASE' => 7, 'OR REPLACE' => 7, - 'RIGHT JOIN' => 7, + 'CROSS JOIN' => 7, 'INNER JOIN' => 7, 'LINEAR KEY' => 7, 'NO RELEASE' => 7, + 'OR REPLACE' => 7, 'RIGHT JOIN' => 7, 'LINEAR HASH' => 7, - 'AND NO CHAIN' => 7, 'FOR EACH ROW' => 7, 'PARTITION BY' => 7, - 'SET PASSWORD' => 7, 'SQL SECURITY' => 7, + 'AND NO CHAIN' => 7, 'FOR EACH ROW' => 7, 'NATURAL JOIN' => 7, + 'PARTITION BY' => 7, 'SET PASSWORD' => 7, 'SQL SECURITY' => 7, 'CHARACTER SET' => 7, 'IF NOT EXISTS' => 7, 'DATA DIRECTORY' => 7, 'UNION DISTINCT' => 7, 'DEFAULT CHARSET' => 7, 'DEFAULT COLLATE' => 7, 'FULL OUTER JOIN' => 7, 'INDEX DIRECTORY' => 7, 'LEFT OUTER JOIN' => 7, 'SUBPARTITION BY' => 7, 'GENERATED ALWAYS' => 7, 'RIGHT OUTER JOIN' => 7, - 'START TRANSACTION' => 7, - 'SELECT TRANSACTION' => 7, + 'NATURAL LEFT JOIN' => 7, 'START TRANSACTION' => 7, + 'NATURAL RIGHT JOIN' => 7, 'SELECT TRANSACTION' => 7, 'DEFAULT CHARACTER SET' => 7, - 'WITH CONSISTENT SNAPSHOT' => 7, + 'NATURAL LEFT OUTER JOIN' => 7, + 'NATURAL RIGHT OUTER JOIN' => 7, 'WITH CONSISTENT SNAPSHOT' => 7, 'BIT' => 9, 'XML' => 9, 'ENUM' => 9, 'JSON' => 9, 'TEXT' => 9, diff --git a/src/Contexts/ContextMySql50700.php b/src/Contexts/ContextMySql50700.php index 8682032..04f410b 100644 --- a/src/Contexts/ContextMySql50700.php +++ b/src/Contexts/ContextMySql50700.php @@ -175,20 +175,21 @@ class ContextMySql50700 extends Context 'AND CHAIN' => 7, 'FULL JOIN' => 7, 'IF EXISTS' => 7, 'LEFT JOIN' => 7, 'LESS THAN' => 7, 'NO ACTION' => 7, 'ON DELETE' => 7, 'ON UPDATE' => 7, 'UNION ALL' => 7, - 'INNER JOIN' => 7, 'LINEAR KEY' => 7, 'NO RELEASE' => 7, 'OR REPLACE' => 7, - 'RIGHT JOIN' => 7, + 'CROSS JOIN' => 7, 'INNER JOIN' => 7, 'LINEAR KEY' => 7, 'NO RELEASE' => 7, + 'OR REPLACE' => 7, 'RIGHT JOIN' => 7, 'LINEAR HASH' => 7, - 'AND NO CHAIN' => 7, 'FOR EACH ROW' => 7, 'PARTITION BY' => 7, - 'SET PASSWORD' => 7, 'SQL SECURITY' => 7, + 'AND NO CHAIN' => 7, 'FOR EACH ROW' => 7, 'NATURAL JOIN' => 7, + 'PARTITION BY' => 7, 'SET PASSWORD' => 7, 'SQL SECURITY' => 7, 'CHARACTER SET' => 7, 'IF NOT EXISTS' => 7, 'DATA DIRECTORY' => 7, 'UNION DISTINCT' => 7, 'DEFAULT CHARSET' => 7, 'DEFAULT COLLATE' => 7, 'FULL OUTER JOIN' => 7, 'INDEX DIRECTORY' => 7, 'LEFT OUTER JOIN' => 7, 'SUBPARTITION BY' => 7, 'GENERATED ALWAYS' => 7, 'RIGHT OUTER JOIN' => 7, - 'START TRANSACTION' => 7, - 'SELECT TRANSACTION' => 7, + 'NATURAL LEFT JOIN' => 7, 'START TRANSACTION' => 7, + 'NATURAL RIGHT JOIN' => 7, 'SELECT TRANSACTION' => 7, 'DEFAULT CHARACTER SET' => 7, - 'WITH CONSISTENT SNAPSHOT' => 7, + 'NATURAL LEFT OUTER JOIN' => 7, + 'NATURAL RIGHT OUTER JOIN' => 7, 'WITH CONSISTENT SNAPSHOT' => 7, 'BIT' => 9, 'XML' => 9, 'ENUM' => 9, 'JSON' => 9, 'TEXT' => 9, diff --git a/src/Lexer.php b/src/Lexer.php index 7fb2356..9de2a7a 100644 --- a/src/Lexer.php +++ b/src/Lexer.php @@ -76,7 +76,7 @@ class Lexer 'parseDelimiter', 'parseWhitespace', 'parseNumber', 'parseComment', 'parseOperator', 'parseBool', 'parseString', - 'parseSymbol', 'parseKeyword', 'parseUnknown' + 'parseSymbol', 'parseKeyword', 'parseLabel', 'parseUnknown' ); /** @@ -442,6 +442,61 @@ class Lexer } /** + * Parses a label. + * + * @return Token + */ + public function parseLabel() + { + $token = ''; + + /** + * Value to be returned. + * + * @var Token $ret + */ + $ret = null; + + /** + * The value of `$this->last` where `$token` ends in `$this->str`. + * + * @var int $iEnd + */ + $iEnd = $this->last; + + /** + * Whether last parsed character is a whitespace. + * + * @var bool $lastSpace + */ + $lastSpace = false; + + for ($j = 1; $j < Context::LABEL_MAX_LENGTH && $this->last < $this->len; ++$j, ++$this->last) { + // Composed keywords shouldn't have more than one whitespace between + // keywords. + if (Context::isWhitespace($this->str[$this->last])) { + if ($lastSpace) { + --$j; // The size of the keyword didn't increase. + continue; + } else { + $lastSpace = true; + } + } elseif ($this->str[$this->last] === ':') { + $token .= $this->str[$this->last]; + $ret = new Token($token, Token::TYPE_LABEL); + $iEnd = $this->last; + break; + } else { + $lastSpace = false; + } + $token .= $this->str[$this->last]; + } + + $this->last = $iEnd; + return $ret; + } + + /** * Parses an operator. * * @return Token diff --git a/src/Parser.php b/src/Parser.php index 21e1b39..0d48a41 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -154,6 +154,10 @@ class Parser 'field' => 'tables', 'options' => array('parseField' => 'table'), ), + 'CROSS JOIN' => array( + 'class' => 'SqlParser\\Components\\JoinKeyword', + 'field' => 'join', + ), 'DROP' => array( 'class' => 'SqlParser\\Components\\ExpressionArray', 'field' => 'fields', @@ -213,7 +217,23 @@ class Parser 'class' => 'SqlParser\\Components\\JoinKeyword', 'field' => 'join', ), - 'STRAIGHT_JOIN' => array( + 'NATURAL JOIN' => array( + 'class' => 'SqlParser\\Components\\JoinKeyword', + 'field' => 'join', + ), + 'NATURAL LEFT JOIN' => array( + 'class' => 'SqlParser\\Components\\JoinKeyword', + 'field' => 'join', + ), + 'NATURAL RIGHT JOIN' => array( + 'class' => 'SqlParser\\Components\\JoinKeyword', + 'field' => 'join', + ), + 'NATURAL LEFT OUTER JOIN' => array( + 'class' => 'SqlParser\\Components\\JoinKeyword', + 'field' => 'join', + ), + 'NATURAL RIGHT OUTER JOIN' => array( 'class' => 'SqlParser\\Components\\JoinKeyword', 'field' => 'join', ), diff --git a/src/Statements/SelectStatement.php b/src/Statements/SelectStatement.php index 34a68c3..8ff0b45 100644 --- a/src/Statements/SelectStatement.php +++ b/src/Statements/SelectStatement.php @@ -82,30 +82,35 @@ class SelectStatement extends Statement * @var array */ public static $CLAUSES = array( - 'SELECT' => array('SELECT', 2), + 'SELECT' => array('SELECT', 2), // Used for options. - '_OPTIONS' => array('_OPTIONS', 1), + '_OPTIONS' => array('_OPTIONS', 1), // Used for selected expressions. - '_SELECT' => array('SELECT', 1), - 'INTO' => array('INTO', 3), - 'FROM' => array('FROM', 3), - 'PARTITION' => array('PARTITION', 3), - - 'JOIN' => array('JOIN', 1), - 'FULL JOIN' => array('FULL JOIN', 1), - 'INNER JOIN' => array('INNER JOIN', 1), - 'LEFT JOIN' => array('LEFT JOIN', 1), - 'LEFT OUTER JOIN' => array('LEFT OUTER JOIN', 1), - 'RIGHT JOIN' => array('RIGHT JOIN', 1), - 'RIGHT OUTER JOIN' => array('RIGHT OUTER JOIN', 1), - - 'WHERE' => array('WHERE', 3), - 'GROUP BY' => array('GROUP BY', 3), - 'HAVING' => array('HAVING', 3), - 'ORDER BY' => array('ORDER BY', 3), - 'LIMIT' => array('LIMIT', 3), - 'PROCEDURE' => array('PROCEDURE', 3), - 'UNION' => array('UNION', 1), + '_SELECT' => array('SELECT', 1), + 'INTO' => array('INTO', 3), + 'FROM' => array('FROM', 3), + 'PARTITION' => array('PARTITION', 3), + + 'JOIN' => array('JOIN', 1), + 'FULL JOIN' => array('FULL JOIN', 1), + 'INNER JOIN' => array('INNER JOIN', 1), + 'LEFT JOIN' => array('LEFT JOIN', 1), + 'LEFT OUTER JOIN' => array('LEFT OUTER JOIN', 1), + 'RIGHT JOIN' => array('RIGHT JOIN', 1), + 'RIGHT OUTER JOIN' => array('RIGHT OUTER JOIN', 1), + 'NATURAL JOIN' => array('NATURAL JOIN', 1), + 'NATURAL LEFT JOIN' => array('NATURAL LEFT JOIN', 1), + 'NATURAL RIGHT JOIN' => array('NATURAL RIGHT JOIN', 1), + 'NATURAL LEFT OUTER JOIN' => array('NATURAL LEFT OUTER JOIN', 1), + 'NATURAL RIGHT OUTER JOIN' => array('NATURAL RIGHT JOIN', 1), + + 'WHERE' => array('WHERE', 3), + 'GROUP BY' => array('GROUP BY', 3), + 'HAVING' => array('HAVING', 3), + 'ORDER BY' => array('ORDER BY', 3), + 'LIMIT' => array('LIMIT', 3), + 'PROCEDURE' => array('PROCEDURE', 3), + 'UNION' => array('UNION', 1), // These are available only when `UNION` is present. // 'ORDER BY' => array('ORDER BY', 3), // 'LIMIT' => array('LIMIT', 3), diff --git a/src/Token.php b/src/Token.php index 3181a44..e5001ef 100644 --- a/src/Token.php +++ b/src/Token.php @@ -119,6 +119,18 @@ class Token */ const TYPE_DELIMITER = 9; + /** + * Labels in LOOP statement, ITERATE statement etc. + * For example (only for begin label): + * begin_label: BEGIN [statement_list] END [end_label] + * begin_label: LOOP [statement_list] END LOOP [end_label] + * begin_label: REPEAT [statement_list] ... END REPEAT [end_label] + * begin_label: WHILE ... DO [statement_list] END WHILE [end_label] + * + * @var int + */ + const TYPE_LABEL = 10; + // Flags that describe the tokens in more detail. // All keywords must have flag 1 so `Context::isKeyword` method doesn't // require strict comparison. |