summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDan Ungureanu <udan1107@gmail.com>2015-07-15 00:19:14 +0300
committerDan Ungureanu <udan1107@gmail.com>2015-07-15 00:19:14 +0300
commitd8c6b9cc361a610ef04df9d86b61aabbb68eacb3 (patch)
treed9e6ab71497b777fc58fc1715315e56ccd934684 /src
parent380603a8bfebd612bbc70801d99eb727be3e36ce (diff)
downloadsql-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.php22
-rw-r--r--src/Components/Array2d.php85
-rw-r--r--src/Components/Expression.php40
-rw-r--r--src/Components/Limit.php4
-rw-r--r--src/Components/OptionsArray.php10
-rw-r--r--src/Components/ParameterDefinition.php1
-rw-r--r--src/Components/RenameOperation.php60
-rw-r--r--src/Components/SetOperation.php24
-rw-r--r--src/Parser.php2
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.