Skip to content

Commit 67067a5

Browse files
committed
Added MySQL utility statements.
Added support for SET statement. Reserved keyword preceded by a dot are no longer considered reserved words. Improved error reporting for statements. A statement is no longer considered unrecognized if the parser doesn't have a full definition of it. Duplicated clauses are reported.
1 parent e603c28 commit 67067a5

9 files changed

Lines changed: 131 additions & 29 deletions

File tree

src/Lexer.php

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -251,22 +251,32 @@ public function lex()
251251
$this->str[$this->last],
252252
$this->last
253253
);
254-
} elseif (($token->type === Token::TYPE_SYMBOL)
254+
} elseif (($lastToken !== null)
255+
&& ($token->type === Token::TYPE_SYMBOL)
255256
&& ($token->flags & Token::FLAG_SYMBOL_VARIABLE)
256-
&& ($lastToken !== null)
257+
&& (($lastToken->type === Token::TYPE_STRING)
258+
|| (($lastToken->type === Token::TYPE_SYMBOL)
259+
&& ($lastToken->flags & Token::FLAG_SYMBOL_BACKTICK)))
257260
) {
258261
// Handles ```... FROM 'user'@'%' ...```.
259-
if ((($lastToken->type === Token::TYPE_SYMBOL)
260-
&& ($lastToken->flags & Token::FLAG_SYMBOL_BACKTICK))
261-
|| ($lastToken->type === Token::TYPE_STRING)
262-
) {
263-
$lastToken->token .= $token->token;
264-
$lastToken->type = Token::TYPE_SYMBOL;
265-
$lastToken->flags = Token::FLAG_SYMBOL_USER;
266-
$lastToken->value .= '@' . $token->value;
267-
continue;
268-
}
262+
$lastToken->token .= $token->token;
263+
$lastToken->type = Token::TYPE_SYMBOL;
264+
$lastToken->flags = Token::FLAG_SYMBOL_USER;
265+
$lastToken->value .= '@' . $token->value;
266+
continue;
267+
} elseif (($lastToken !== null)
268+
&& ($token->type === Token::TYPE_KEYWORD)
269+
&& ($token->flags & Token::FLAG_KEYWORD_RESERVED)
270+
&& ($lastToken->type === Token::TYPE_OPERATOR)
271+
&& ($lastToken->value === '.')
272+
) {
273+
// Handles ```... tbl.FROM ...```. In this case, FROM is not
274+
// a reserved word.
275+
$token->type = Token::TYPE_NONE;
276+
$token->flags = 0;
277+
$token->value = $token->token;
269278
}
279+
270280
$token->position = $lastIdx;
271281

272282
$list->tokens[$list->count++] = $token;

src/Parser.php

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,12 @@ class Parser
5151
*/
5252
public static $STATEMENT_PARSERS = array(
5353

54+
// MySQL Utility Statements
5455
'EXPLAIN' => 'SqlParser\\Statements\\ExplainStatement',
56+
'DESCRIBE' => 'SqlParser\\Statements\\ExplainStatement',
57+
'HELP' => '',
58+
'USE' => '',
59+
'STATUS' => '',
5560

5661
// Table Maintenance Statements
5762
// https://dev.mysql.com/doc/refman/5.7/en/table-maintenance-sql.html
@@ -65,7 +70,7 @@ class Parser
6570

6671
// Database Administration Statements
6772
// https://dev.mysql.com/doc/refman/5.7/en/sql-syntax-server-administration.html
68-
'SET' => '',
73+
'SET' => 'SqlParser\\Statements\\SetStatement',
6974
'SHOW' => 'SqlParser\\Statements\\ShowStatement',
7075

7176
// Data Definition Statements.
@@ -386,10 +391,15 @@ public function parse()
386391

387392
// Checking if it is a known statement that can be parsed.
388393
if (empty(static::$STATEMENT_PARSERS[$token->value])) {
389-
$this->error(
390-
__('Unrecognized statement type.'),
391-
$token
392-
);
394+
if (!isset(static::$STATEMENT_PARSERS[$token->value])) {
395+
// A statement is considered recognized if the parser
396+
// is aware that it is a statement, but it does not have
397+
// a parser for it yet.
398+
$this->error(
399+
__('Unrecognized statement type.'),
400+
$token
401+
);
402+
}
393403
// Skipping to the end of this statement.
394404
$list->getNextOfType(Token::TYPE_DELIMITER);
395405
//

src/Statement.php

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -165,14 +165,11 @@ public function build()
165165
public function parse(Parser $parser, TokensList $list)
166166
{
167167
/**
168-
* Whether the beginning of this statement was previously parsed.
169-
*
170-
* This is used to delimit statements that don't use the usual
171-
* delimiters.
172-
*
173-
* @var bool
168+
* Array containing all list of clauses parsed.
169+
* This is used to check for duplicates.
170+
* @var array
174171
*/
175-
$parsedBeginning = false;
172+
$parsedClauses = array();
176173

177174
// This may be corrected by the parser.
178175
$this->first = $list->idx;
@@ -232,6 +229,20 @@ public function parse(Parser $parser, TokensList $list)
232229
*/
233230
$options = array();
234231

232+
// Looking for duplicated clauses.
233+
if ((!empty(Parser::$KEYWORD_PARSERS[$token->value]))
234+
|| (!empty(Parser::$STATEMENT_PARSERS[$token->value]))
235+
) {
236+
if (!empty($parsedClauses[$token->value])) {
237+
$parser->error(
238+
__('This type of clause was previously parsed.'), $token
239+
);
240+
break;
241+
}
242+
$parsedClauses[$token->value] = true;
243+
}
244+
245+
// Checking if this is the beginning of a clause.
235246
if (!empty(Parser::$KEYWORD_PARSERS[$token->value])) {
236247
$class = Parser::$KEYWORD_PARSERS[$token->value]['class'];
237248
$field = Parser::$KEYWORD_PARSERS[$token->value]['field'];
@@ -240,17 +251,22 @@ public function parse(Parser $parser, TokensList $list)
240251
}
241252
}
242253

254+
// Checking if this is the beginning of the statement.
243255
if (!empty(Parser::$STATEMENT_PARSERS[$token->value])) {
244-
if ($parsedBeginning) {
245-
// New statement has started. We let the parser construct a
246-
// new statement and parse that one
256+
if ((!empty(static::$CLAUSES)) // Undefined for some statements.
257+
&& (empty(static::$CLAUSES[$token->value]))
258+
) {
259+
// Some keywords (e.g. `SET`) may be the beginning of a
260+
// statement and a clause.
261+
// If such keyword was found and it cannot be a clause of
262+
// this statement it means it is a new statement, but no
263+
// delimiter was found between them.
247264
$parser->error(
248265
__('A new statement was found, but no delimiter between them.'),
249266
$token
250267
);
251268
break;
252269
}
253-
$parsedBeginning = true;
254270
if (!$parsedOptions) {
255271
if (empty(static::$OPTIONS[$token->value])) {
256272
// Skipping keyword because if it is not a option.

src/Statements/ReplaceStatement.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,22 @@ class ReplaceStatement extends Statement
4545
'DELAYED' => 1,
4646
);
4747

48+
/**
49+
* The clauses of this statement, in order.
50+
*
51+
* @see Statement::$CLAUSES
52+
*
53+
* @var array
54+
*/
55+
public static $CLAUSES = array(
56+
'REPLACE' => array('REPLACE', 2),
57+
// Used for options.
58+
'_OPTIONS' => array('_OPTIONS', 1),
59+
'INTO' => array('FROM', 3),
60+
'VALUES' => array('VALUES', 1),
61+
'SET' => array('PARTITION', 3),
62+
);
63+
4864
/**
4965
* Tables used as target for this statement.
5066
*

src/Statements/SetStatement.php

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<?php
2+
3+
/**
4+
* `SET` statement.
5+
*
6+
* @package SqlParser
7+
* @subpackage Statements
8+
*/
9+
namespace SqlParser\Statements;
10+
11+
use SqlParser\Statement;
12+
use SqlParser\Components\Expression;
13+
use SqlParser\Components\Limit;
14+
use SqlParser\Components\OrderKeyword;
15+
use SqlParser\Components\SetOperation;
16+
use SqlParser\Components\Condition;
17+
18+
/**
19+
* `SET` statement.
20+
*
21+
* @category Statements
22+
* @package SqlParser
23+
* @subpackage Statements
24+
* @author Dan Ungureanu <udan1107@gmail.com>
25+
* @license http://opensource.org/licenses/GPL-2.0 GNU Public License
26+
*/
27+
class SetStatement extends Statement
28+
{
29+
30+
/**
31+
* The clauses of this statement, in order.
32+
*
33+
* @see Statement::$CLAUSES
34+
*
35+
* @var array
36+
*/
37+
public static $CLAUSES = array(
38+
'SET' => array('SET', 3),
39+
);
40+
41+
/**
42+
* The updated values.
43+
*
44+
* @var SetOperation[]
45+
*/
46+
public $set;
47+
}

tests/Lexer/LexerTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public function testLexProvider()
6565
array('lexer/lexDelimiterErr1'),
6666
array('lexer/lexDelimiterErr2'),
6767
array('lexer/lexKeyword'),
68+
array('lexer/lexKeyword2'),
6869
array('lexer/lexNumber'),
6970
array('lexer/lexOperator'),
7071
array('lexer/lexString'),

tests/data/bugs/gh9.out

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@ COMMIT;";s:3:"len";i:213;s:4:"last";i:213;s:4:"list";O:20:"SqlParser\TokensList"
3131
";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:164;}i:55;O:15:"SqlParser\Token":5:{s:5:"token";s:5:"INDEX";s:5:"value";s:5:"INDEX";s:4:"type";i:1;s:5:"flags";i:19;s:8:"position";i:165;}i:56;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:170;}i:57;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"(";s:5:"value";s:1:"(";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:171;}i:58;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:172;}i:59;O:15:"SqlParser\Token":5:{s:5:"token";s:10:"`position`";s:5:"value";s:8:"position";s:4:"type";i:8;s:5:"flags";i:2;s:8:"position";i:173;}i:60;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:183;}i:61;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:184;}i:62;O:15:"SqlParser\Token":5:{s:5:"token";s:1:"
3232
";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:185;}i:63;O:15:"SqlParser\Token":5:{s:5:"token";s:1:")";s:5:"value";s:1:")";s:4:"type";i:2;s:5:"flags";i:16;s:8:"position";i:186;}i:64;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:187;}i:65;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"ENGINE";s:5:"value";s:6:"ENGINE";s:4:"type";i:1;s:5:"flags";i:1;s:8:"position";i:188;}i:66;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:194;}i:67;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:195;}i:68;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:196;}i:69;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"InnoDB";s:5:"value";s:6:"InnoDB";s:4:"type";i:0;s:5:"flags";i:0;s:8:"position";i:197;}i:70;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:203;}i:71;O:15:"SqlParser\Token":5:{s:5:"token";s:2:"
3333

34-
";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:204;}i:72;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"COMMIT";s:5:"value";s:6:"COMMIT";s:4:"type";i:1;s:5:"flags";i:1;s:8:"position";i:206;}i:73;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:212;}i:74;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:75;s:3:"idx";i:75;}s:9:"delimiter";s:1:";";s:12:"delimiterLen";i:1;s:6:"errors";a:0:{}}s:6:"parser";O:16:"SqlParser\Parser":4:{s:4:"list";r:8;s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:3:{i:0;O:36:"SqlParser\Statements\SelectStatement":15:{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:3:"foo";s:6:"column";N;s:4:"expr";s:3:"foo";s:5:"alias";N;s:8:"function";N;s:8:"subquery";N;}}s:9:"partition";N;s:5:"where";a:0:{}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";N;s:5:"union";a:0:{}s:7:"options";O:33:"SqlParser\Components\OptionsArray":1:{s:7:"options";a:0:{}}s:5:"first";i:0;s:4:"last";i:9;}i:1;O:36:"SqlParser\Statements\SelectStatement":15:{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:3:"foo";s:6:"column";N;s:4:"expr";s:3:"foo";s:5:"alias";N;s:8:"function";N;s:8:"subquery";N;}}s:9:"partition";N;s:5:"where";a:0:{}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";N;s:5:"union";a:0:{}s:7:"options";O:33:"SqlParser\Components\OptionsArray":1:{s:7:"options";a:0:{}}s:5:"first";i:10;s:4:"last";i:19;}i:2;O:41:"SqlParser\Statements\TransactionStatement":6:{s:4:"type";i:1;s:10:"statements";a:1:{i:0;O:36:"SqlParser\Statements\CreateStatement":10:{s:4:"name";O:31:"SqlParser\Components\Expression":7:{s:8:"database";N;s:5:"table";s:2:"tb";s:6:"column";N;s:4:"expr";s:4:"`tb`";s:5:"alias";N;s:8:"function";N;s:8:"subquery";N;}s:13:"entityOptions";O:33:"SqlParser\Components\OptionsArray":1:{s:7:"options";a:0:{}}s:6:"fields";a:1:{i:0;O:36:"SqlParser\Components\FieldDefinition":6:{s:4:"name";s:3:"uid";s:12:"isConstraint";N;s:4:"type";O:29:"SqlParser\Components\DataType":3:{s:4:"name";s:3:"INT";s:10:"parameters";a:0:{}s:7:"options";O:33:"SqlParser\Components\OptionsArray":1:{s:7:"options";a:1:{i:4;s:8:"UNSIGNED";}}}s:3:"key";N;s:10:"references";N;s:7:"options";O:33:"SqlParser\Components\OptionsArray":1:{s:7:"options";a:1:{i:1;s:8:"NOT NULL";}}}}s:5:"table";N;s:6:"return";N;s:10:"parameters";N;s:4:"body";a:0:{}s:7:"options";O:33:"SqlParser\Components\OptionsArray":1:{s:7:"options";a:1:{i:6;s:5:"TABLE";}}s:5:"first";i:21;s:4:"last";i:37;}}s:3:"end";O:41:"SqlParser\Statements\TransactionStatement":6:{s:4:"type";i:2;s:10:"statements";N;s:3:"end";N;s:7:"options";O:33:"SqlParser\Components\OptionsArray":1:{s:7:"options";a:1:{i:1;s:6:"COMMIT";}}s:5:"first";i:72;s:4:"last";i:72;}s:7:"options";O:33:"SqlParser\Components\OptionsArray":1:{s:7:"options";a:1:{i:1;s:17:"START TRANSACTION";}}s:5:"first";i:20;s:4:"last";i:20;}}}s:6:"errors";a:2:{s:5:"lexer";a:0:{}s:6:"parser";a:5:{i:0;a:3:{i:0;s:57:"A new statement was found, but no delimiter between them.";i:1;r:70;i:2;i:0;}i:1;a:3:{i:0;s:57:"A new statement was found, but no delimiter between them.";i:1;r:130;i:2;i:0;}i:2;a:3:{i:0;s:42:"A comma or a closing bracket was expected.";i:1;r:238;i:2;i:0;}i:3;a:3:{i:0;s:34:"Unexpected beginning of statement.";i:1;r:238;i:2;i:0;}i:4;a:3:{i:0;s:28:"Unrecognized statement type.";i:1;r:250;i:2;i:0;}}}}
34+
";s:5:"value";s:1:" ";s:4:"type";i:3;s:5:"flags";i:0;s:8:"position";i:204;}i:72;O:15:"SqlParser\Token":5:{s:5:"token";s:6:"COMMIT";s:5:"value";s:6:"COMMIT";s:4:"type";i:1;s:5:"flags";i:1;s:8:"position";i:206;}i:73;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:212;}i:74;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:75;s:3:"idx";i:75;}s:9:"delimiter";s:1:";";s:12:"delimiterLen";i:1;s:6:"errors";a:0:{}}s:6:"parser";O:16:"SqlParser\Parser":4:{s:4:"list";r:8;s:6:"strict";b:0;s:6:"errors";a:0:{}s:10:"statements";a:3:{i:0;O:36:"SqlParser\Statements\SelectStatement":15:{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:3:"foo";s:6:"column";N;s:4:"expr";s:3:"foo";s:5:"alias";N;s:8:"function";N;s:8:"subquery";N;}}s:9:"partition";N;s:5:"where";a:0:{}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";N;s:5:"union";a:0:{}s:7:"options";O:33:"SqlParser\Components\OptionsArray":1:{s:7:"options";a:0:{}}s:5:"first";i:0;s:4:"last";i:9;}i:1;O:36:"SqlParser\Statements\SelectStatement":15:{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:3:"foo";s:6:"column";N;s:4:"expr";s:3:"foo";s:5:"alias";N;s:8:"function";N;s:8:"subquery";N;}}s:9:"partition";N;s:5:"where";a:0:{}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";N;s:5:"union";a:0:{}s:7:"options";O:33:"SqlParser\Components\OptionsArray":1:{s:7:"options";a:0:{}}s:5:"first";i:10;s:4:"last";i:19;}i:2;O:41:"SqlParser\Statements\TransactionStatement":6:{s:4:"type";i:1;s:10:"statements";a:1:{i:0;O:36:"SqlParser\Statements\CreateStatement":10:{s:4:"name";O:31:"SqlParser\Components\Expression":7:{s:8:"database";N;s:5:"table";s:2:"tb";s:6:"column";N;s:4:"expr";s:4:"`tb`";s:5:"alias";N;s:8:"function";N;s:8:"subquery";N;}s:13:"entityOptions";O:33:"SqlParser\Components\OptionsArray":1:{s:7:"options";a:0:{}}s:6:"fields";a:1:{i:0;O:36:"SqlParser\Components\FieldDefinition":6:{s:4:"name";s:3:"uid";s:12:"isConstraint";N;s:4:"type";O:29:"SqlParser\Components\DataType":3:{s:4:"name";s:3:"INT";s:10:"parameters";a:0:{}s:7:"options";O:33:"SqlParser\Components\OptionsArray":1:{s:7:"options";a:1:{i:4;s:8:"UNSIGNED";}}}s:3:"key";N;s:10:"references";N;s:7:"options";O:33:"SqlParser\Components\OptionsArray":1:{s:7:"options";a:1:{i:1;s:8:"NOT NULL";}}}}s:5:"table";N;s:6:"return";N;s:10:"parameters";N;s:4:"body";a:0:{}s:7:"options";O:33:"SqlParser\Components\OptionsArray":1:{s:7:"options";a:1:{i:6;s:5:"TABLE";}}s:5:"first";i:21;s:4:"last";i:37;}}s:3:"end";O:41:"SqlParser\Statements\TransactionStatement":6:{s:4:"type";i:2;s:10:"statements";N;s:3:"end";N;s:7:"options";O:33:"SqlParser\Components\OptionsArray":1:{s:7:"options";a:1:{i:1;s:6:"COMMIT";}}s:5:"first";i:72;s:4:"last";i:72;}s:7:"options";O:33:"SqlParser\Components\OptionsArray":1:{s:7:"options";a:1:{i:1;s:17:"START TRANSACTION";}}s:5:"first";i:20;s:4:"last";i:20;}}}s:6:"errors";a:2:{s:5:"lexer";a:0:{}s:6:"parser";a:5:{i:0;a:3:{i:0;s:42:"This type of clause was previously parsed.";i:1;r:70;i:2;i:0;}i:1;a:3:{i:0;s:57:"A new statement was found, but no delimiter between them.";i:1;r:130;i:2;i:0;}i:2;a:3:{i:0;s:42:"A comma or a closing bracket was expected.";i:1;r:238;i:2;i:0;}i:3;a:3:{i:0;s:34:"Unexpected beginning of statement.";i:1;r:238;i:2;i:0;}i:4;a:3:{i:0;s:28:"Unrecognized statement type.";i:1;r:250;i:2;i:0;}}}}

tests/data/lexer/lexKeyword2.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
SELECT tbl.TABLE FROM tbl

0 commit comments

Comments
 (0)