diff options
author | Dan Ungureanu <udan1107@gmail.com> | 2015-07-15 00:19:14 +0300 |
---|---|---|
committer | Dan Ungureanu <udan1107@gmail.com> | 2015-07-15 00:19:14 +0300 |
commit | d8c6b9cc361a610ef04df9d86b61aabbb68eacb3 (patch) | |
tree | d9e6ab71497b777fc58fc1715315e56ccd934684 /src | |
parent | 380603a8bfebd612bbc70801d99eb727be3e36ce (diff) | |
download | sql-parser-d8c6b9cc361a610ef04df9d86b61aabbb68eacb3.zip sql-parser-d8c6b9cc361a610ef04df9d86b61aabbb68eacb3.tar.gz sql-parser-d8c6b9cc361a610ef04df9d86b61aabbb68eacb3.tar.bz2 |
Improved error messages.
Achieved 100% code coverage.
Some refactoring.
Diffstat (limited to 'src')
-rw-r--r-- | src/Components/AlterOperation.php | 22 | ||||
-rw-r--r-- | src/Components/Array2d.php | 85 | ||||
-rw-r--r-- | src/Components/Expression.php | 40 | ||||
-rw-r--r-- | src/Components/Limit.php | 4 | ||||
-rw-r--r-- | src/Components/OptionsArray.php | 10 | ||||
-rw-r--r-- | src/Components/ParameterDefinition.php | 1 | ||||
-rw-r--r-- | src/Components/RenameOperation.php | 60 | ||||
-rw-r--r-- | src/Components/SetOperation.php | 24 | ||||
-rw-r--r-- | src/Parser.php | 2 |
9 files changed, 141 insertions, 107 deletions
diff --git a/src/Components/AlterOperation.php b/src/Components/AlterOperation.php index 7c52828..9bb56cf 100644 --- a/src/Components/AlterOperation.php +++ b/src/Components/AlterOperation.php @@ -139,12 +139,19 @@ class AlterOperation extends Component break; } - // Skipping whitespaces and comments. - if (($token->type === Token::TYPE_WHITESPACE) || ($token->type === Token::TYPE_COMMENT)) { - if ($state !== 2) { - // State 2 parses the unknown part which must include whitespaces as well. - continue; + // Skipping comments. + if ($token->type === Token::TYPE_COMMENT) { + continue; + } + + // Skipping whitespaces. + if ($token->type === Token::TYPE_WHITESPACE) { + if ($state === 2) { + // When parsing the unknown part, the whitespaces are + // included to not break anything. + $ret->unknown[] = $token; } + continue; } if ($state === 0) { @@ -179,6 +186,11 @@ class AlterOperation extends Component } } + if ($ret->options->isEmpty()) { + $parser->error("Unrecognized ALTER operation.", $list->tokens[$list->idx]); + return null; + } + --$list->idx; return $ret; } diff --git a/src/Components/Array2d.php b/src/Components/Array2d.php index c521de2..90be806 100644 --- a/src/Components/Array2d.php +++ b/src/Components/Array2d.php @@ -26,39 +26,38 @@ class Array2d extends Component { /** - * An array with the values of the row to be inserted. - * - * @var array - */ - public $values; - - /** * @param Parser $parser The parser that serves as context. * @param TokensList $list The list of tokens that are being parsed. * @param array $options Parameters for parsing. * - * @return Array2d + * @return ArrayObj[] */ public static function parse(Parser $parser, TokensList $list, array $options = array()) { $ret = array(); - $expr = new Array2d(); - $value = ''; + /** + * Whether an array was parsed or not. To be a valid parsing, at least + * one array must be parsed after each comma. + * @var bool $parsed + */ + $parsed = false; + + /** + * The number of values in each set. + * @var int + */ + $count = -1; /** * The state of the parser. * * Below are the states of the parser. * - * 0 ------------------------[ ( ]-----------------------> 1 + * 0 ----------------------[ array ]---------------------> 1 * - * 1 ----------------------[ value ]---------------------> 2 - * - * 2 ------------------------[ , ]-----------------------> 1 - * 2 ------------------------[ ) ]-----------------------> 3 - * - * 3 ---------------------[ options ]--------------------> 4 + * 1 ------------------------[ , ]------------------------> 0 + * 1 -----------------------[ else ]----------------------> -1 * * @var int */ @@ -86,40 +85,36 @@ class Array2d extends Component break; } - if ($token->type === Token::TYPE_OPERATOR) { + if ($state === 0) { if ($token->value === '(') { - $state = 1; - continue; - } elseif ($token->value === ',') { - if ($state !== 3) { - $expr->values[] = $value; - $value = ''; - $state = 1; + $arr = ArrayObj::parse($parser, $list, $options); + $arrCount = count($arr->values); + if ($count === -1) { + $count = $arrCount; + } elseif ($arrCount != $count) { + $parser->error("Expected {$count} values, found {$arrCount}.", $token); } - continue; - } elseif ($token->value === ')') { - $state = 3; - $expr->values[] = $value; - $ret[] = $expr; - $value = ''; - $expr = new Array2d(); - continue; + $ret[] = $arr; + $parsed = true; + $state = 1; + } else { + break; + } + } elseif ($state === 1) { + if ($token->value === ',') { + $parsed = false; + $state = 0; + } else { + break; } - - // No other operator is expected. - break; - } - - if ($state === 1) { - $value .= $token->value; - $state = 2; } - } - // Last iteration was not saved. - if (!empty($expr->values)) { - $ret[] = $expr; + if (!$parsed) { + $parser->error( + "Expected open bracket followed by a set of values.", + $list->tokens[$list->idx] + ); } --$list->idx; diff --git a/src/Components/Expression.php b/src/Components/Expression.php index 065810c..dfa3c11 100644 --- a/src/Components/Expression.php +++ b/src/Components/Expression.php @@ -126,9 +126,9 @@ class Expression extends Component /** * Whether a period was previously found. - * @var bool $period + * @var bool $dot */ - $period = false; + $dot = false; /** * Whether an alias is expected. Is 2 if `AS` keyword was found. @@ -169,7 +169,7 @@ class Expression extends Component if (($isExpr) && (!$alias)) { $ret->expr .= $token->token; } - if (($alias === 0) && (empty($options['noAlias'])) && (!$isExpr) && (!$period) && (!empty($ret->expr))) { + if (($alias === 0) && (empty($options['noAlias'])) && (!$isExpr) && (!$dot) && (!empty($ret->expr))) { $alias = 1; } continue; @@ -202,9 +202,6 @@ class Expression extends Component } if ($token->value === '(') { ++$brackets; - // We don't check to see if `$prev` is `true` (open bracket - // was found before) because the brackets count is one (the - // only bracket we found is this one). if ((empty($ret->function)) && ($prev !== null) && ($prev !== true)) { // A function name was previously found and now an open // bracket, so this is a function call. @@ -250,28 +247,36 @@ class Expression extends Component // Found a `.` which means we expect a column name and // the column name we parsed is actually the table name // and the table name is actually a database name. - if ((!empty($ret->database)) || ($period)) { + if ((!empty($ret->database)) || ($dot)) { $parser->error('Unexpected dot.', $token); } $ret->database = $ret->table; $ret->table = $ret->column; $ret->column = null; - $period = true; + $dot = true; } else { // We found the name of a column (or table if column // field should be skipped; used to parse table names). - if (!empty($options['skipColumn'])) { - if (!empty($ret->table)) { + $field = (!empty($options['skipColumn'])) ? 'table' : 'column'; + if (!empty($ret->$field)) { + + // No alias is expected. + if (!empty($options['noAlias'])) { break; } - $ret->table = $token->value; - } else { - if (!empty($ret->column)) { - break; + + // Parsing aliases without `AS` keyword and any whitespace. + // Example: SELECT 1`foo` + if (($token->type === Token::TYPE_STRING) + || (($token->type === Token::TYPE_SYMBOL) + && ($token->flags & Token::FLAG_SYMBOL_BACKTICK)) + ) { + $ret->alias = $token->value; } - $ret->column = $token->value; + } else { + $ret->$field = $token->value; } - $period = false; + $dot = false; } } else { // Parsing aliases without `AS` keyword. @@ -298,9 +303,8 @@ class Expression extends Component } } - if ($alias === 2) { - $parser->error('Alias was expected.'); + $parser->error('Alias was expected.', $list->tokens[$list->idx - 1]); } // Whitespaces might be added at the end. diff --git a/src/Components/Limit.php b/src/Components/Limit.php index 1e25d2a..86ab55a 100644 --- a/src/Components/Limit.php +++ b/src/Components/Limit.php @@ -87,7 +87,7 @@ class Limit extends Component if (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'OFFSET')) { if ($offset) { - $parser->error('An offset was expected.'); + $parser->error('An offset was expected.', $token); } $offset = true; continue; @@ -108,7 +108,7 @@ class Limit extends Component } if ($offset) { - $parser->error('An offset was expected.'); + $parser->error('An offset was expected.', $list->tokens[$list->idx - 1]); } --$list->idx; diff --git a/src/Components/OptionsArray.php b/src/Components/OptionsArray.php index 513355d..2a112ed 100644 --- a/src/Components/OptionsArray.php +++ b/src/Components/OptionsArray.php @@ -319,4 +319,14 @@ class OptionsArray extends Component $this->options = array_merge_recursive($this->options, $options->options); } } + + /** + * Checks tf there are no options set. + * + * @return bool + */ + public function isEmpty() + { + return empty($this->options); + } } diff --git a/src/Components/ParameterDefinition.php b/src/Components/ParameterDefinition.php index db0f815..d10a460 100644 --- a/src/Components/ParameterDefinition.php +++ b/src/Components/ParameterDefinition.php @@ -120,7 +120,6 @@ class ParameterDefinition extends Component $expr = new ParameterDefinition(); if ($token->value === ',') { $state = 1; - continue; } elseif ($token->value === ')') { ++$list->idx; break; diff --git a/src/Components/RenameOperation.php b/src/Components/RenameOperation.php index 3350437..fc73331 100644 --- a/src/Components/RenameOperation.php +++ b/src/Components/RenameOperation.php @@ -53,6 +53,13 @@ class RenameOperation extends Component $expr = new RenameOperation(); /** + * Whether an operation was parsed or not. To be a valid parsing, at + * least one operation must be parsed after each comma. + * @var bool $parsed + */ + $parsed = false; + + /** * The state of the parser. * * Below are the states of the parser. @@ -87,29 +94,7 @@ class RenameOperation extends Component continue; } - if (($token->type === Token::TYPE_KEYWORD) && ($token->flags & Token::FLAG_KEYWORD_RESERVED)) { - if (($state === 1) && ($token->value === 'TO')) { - $state = 2; - continue; - } - - // No other keyword is expected. - break; - } - - if ($token->type === Token::TYPE_OPERATOR) { - if (($state === 3) && ($token->value === ',')) { - $ret[] = $expr; - $expr = new RenameOperation(); - $state = 0; - continue; - } - - // No other operator is expected. - break; - } - - if ($state == 0) { + if ($state === 0) { $expr->old = Expression::parse( $parser, $list, @@ -119,8 +104,18 @@ class RenameOperation extends Component 'skipColumn' => true, ) ); + if (empty($expr->old)) { + $parser->error('Expected old table name.', $token); + } $state = 1; - } elseif ($state == 2) { + } elseif ($state === 1) { + if (($token->type === Token::TYPE_KEYWORD) && ($token->value === 'TO')) { + $state = 2; + } else { + $parser->error('Expected `TO` keyword.', $token); + break; + } + } elseif ($state === 2) { $expr->new = Expression::parse( $parser, $list, @@ -130,9 +125,26 @@ class RenameOperation extends Component 'noAlias' => true, ) ); + if (empty($expr->new)) { + $parser->error('Expected new table name.', $token); + } $state = 3; + $parsed = true; + } elseif ($state === 3) { + if (($token->type === Token::TYPE_OPERATOR) && ($token->value === ',')) { + $ret[] = $expr; + $expr = new RenameOperation(); + $state = 0; + // Found a comma, looking for another operation. + $parsed = false; + } else { + break; + } } + } + if (!$parsed) { + $parser->error('Expected a rename operation.', $list->tokens[$list->idx - 1]); } // Last iteration was not saved. diff --git a/src/Components/SetOperation.php b/src/Components/SetOperation.php index dad91e5..1cb59a1 100644 --- a/src/Components/SetOperation.php +++ b/src/Components/SetOperation.php @@ -89,24 +89,26 @@ class SetOperation extends Component } if ($token->type === Token::TYPE_OPERATOR) { - if ($token->value === ',') { + + } + + if ($state === 0) { + if ($token->token === '=') { + $state = 1; + } else { + $expr->column .= $token->token; + } + } elseif ($state === 1) { + if ($token->token === ',') { $expr->column = trim($expr->column); $expr->value = trim($expr->value); $ret[] = $expr; $expr = new SetOperation(); $state = 0; - continue; - } elseif ($token->value === '=') { - $state = 1; - continue; + } else { + $expr->value .= $token->token; } } - - if ($state === 0) { - $expr->column .= $token->token; - } else { // } else if ($state === 1) { - $expr->value .= $token->token; - } } // Last iteration was not saved. diff --git a/src/Parser.php b/src/Parser.php index 260ea25..c095d1c 100644 --- a/src/Parser.php +++ b/src/Parser.php @@ -338,7 +338,7 @@ class Parser // Checking if it is a known statement that can be parsed. if (empty(static::$STATEMENT_PARSERS[$token->value])) { $this->error( - 'Unrecognized statement type "' . $token->value . '".', + 'Unrecognized statement type.', $token ); // Skipping to the end of this statement. |