diff --git a/src/Components/AlterOperation.php b/src/Components/AlterOperation.php index 8d134b205..d46a63ec7 100644 --- a/src/Components/AlterOperation.php +++ b/src/Components/AlterOperation.php @@ -237,11 +237,7 @@ class AlterOperation extends Component 'ON COMPLETION PRESERVE' => 5, 'ON COMPLETION NOT PRESERVE' => 5, 'RENAME' => 6, - 'TO' => [ - 7, - 'expr', - ['parseField' => 'table'], - ], + 'TO' => [7, 'expr', ['parseField' => 'table', 'breakOnAlias' => true]], 'ENABLE' => 8, 'DISABLE' => 8, 'DISABLE ON SLAVE' => 8, diff --git a/src/Components/Expression.php b/src/Components/Expression.php index 57037ec98..a817ebee4 100644 --- a/src/Components/Expression.php +++ b/src/Components/Expression.php @@ -273,7 +273,10 @@ public static function parse(Parser $parser, TokensList $list, array $options = } $isExpr = true; - } elseif ($brackets === 0 && strlen((string) $ret->expr) > 0 && ! $alias) { + } elseif ( + $brackets === 0 && strlen((string) $ret->expr) > 0 && ! $alias + && ($ret->table === null || $ret->table === '') + ) { /* End of expression */ break; } diff --git a/src/Statements/LoadStatement.php b/src/Statements/LoadStatement.php index ac2f9fc96..f0f6253e2 100644 --- a/src/Statements/LoadStatement.php +++ b/src/Statements/LoadStatement.php @@ -293,7 +293,7 @@ public function parse(Parser $parser, TokensList $list) } ++$list->idx; - $this->table = Expression::parse($parser, $list, ['parseField' => 'table']); + $this->table = Expression::parse($parser, $list, ['parseField' => 'table', 'breakOnAlias' => true]); $state = 3; } elseif ($state >= 3 && $state <= 7) { if ($token->type === Token::TYPE_KEYWORD) { diff --git a/tests/Misc/BugsTest.php b/tests/Misc/BugsTest.php index 2f93e5c17..95900f686 100644 --- a/tests/Misc/BugsTest.php +++ b/tests/Misc/BugsTest.php @@ -35,6 +35,7 @@ public function bugProvider(): array ['bugs/gh412'], ['bugs/gh478'], ['bugs/gh492'], + ['bugs/gh496'], ['bugs/gh498'], ['bugs/gh499'], ['bugs/gh508'], diff --git a/tests/Utils/ErrorTest.php b/tests/Utils/ErrorTest.php index f3769891c..2a91cdf00 100644 --- a/tests/Utils/ErrorTest.php +++ b/tests/Utils/ErrorTest.php @@ -38,8 +38,12 @@ public function testGetWithNullToken(): void { $lexer = new Lexer('LOCK TABLES table1 AS `t1` LOCAL'); $parser = new Parser($lexer->list); - $this->assertEquals( - [['Unexpected keyword.', 0, 'LOCAL', 27], ['Unexpected end of LOCK expression.', 0, null, null]], + $this->assertSame( + [ + ['An alias was previously found.', 0, 'LOCAL', 27], + ['Unexpected keyword.', 0, 'LOCAL', 27], + ['Unexpected end of LOCK expression.', 0, '', null], + ], Error::get([$lexer, $parser]) ); } diff --git a/tests/data/bugs/gh496.in b/tests/data/bugs/gh496.in new file mode 100644 index 000000000..935f01e17 --- /dev/null +++ b/tests/data/bugs/gh496.in @@ -0,0 +1,3 @@ +SELECT COUNT(*) AS amount +FROM one i +JOIN two io ON io.id = i.id \ No newline at end of file diff --git a/tests/data/bugs/gh496.out b/tests/data/bugs/gh496.out new file mode 100644 index 000000000..624a921f5 --- /dev/null +++ b/tests/data/bugs/gh496.out @@ -0,0 +1,424 @@ +{ + "query": "SELECT COUNT(*) AS amount\nFROM one i\nJOIN two io ON io.id = i.id", + "lexer": { + "@type": "PhpMyAdmin\\SqlParser\\Lexer", + "str": "SELECT COUNT(*) AS amount\nFROM one i\nJOIN two io ON io.id = i.id", + "len": 71, + "last": 71, + "list": { + "@type": "PhpMyAdmin\\SqlParser\\TokensList", + "tokens": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "SELECT", + "value": "SELECT", + "keyword": "SELECT", + "type": 1, + "flags": 3, + "position": 0 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 6 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "COUNT", + "value": "COUNT", + "keyword": "COUNT", + "type": 1, + "flags": 33, + "position": 8 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "(", + "value": "(", + "keyword": null, + "type": 2, + "flags": 16, + "position": 13 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "*", + "value": "*", + "keyword": null, + "type": 2, + "flags": 16, + "position": 14 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": ")", + "value": ")", + "keyword": null, + "type": 2, + "flags": 16, + "position": 15 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 16 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "AS", + "value": "AS", + "keyword": "AS", + "type": 1, + "flags": 3, + "position": 17 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 19 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "amount", + "value": "amount", + "keyword": null, + "type": 0, + "flags": 0, + "position": 20 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "\n", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 26 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "FROM", + "value": "FROM", + "keyword": "FROM", + "type": 1, + "flags": 3, + "position": 27 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 31 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "one", + "value": "one", + "keyword": "ONE", + "type": 1, + "flags": 1, + "position": 35 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 38 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "i", + "value": "i", + "keyword": null, + "type": 0, + "flags": 0, + "position": 39 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "\n", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 40 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "JOIN", + "value": "JOIN", + "keyword": "JOIN", + "type": 1, + "flags": 3, + "position": 41 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 45 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "two", + "value": "two", + "keyword": null, + "type": 0, + "flags": 0, + "position": 49 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 52 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "io", + "value": "io", + "keyword": "IO", + "type": 1, + "flags": 1, + "position": 53 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 55 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "ON", + "value": "ON", + "keyword": "ON", + "type": 1, + "flags": 3, + "position": 56 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 58 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "io", + "value": "io", + "keyword": "IO", + "type": 1, + "flags": 1, + "position": 59 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": ".", + "value": ".", + "keyword": null, + "type": 2, + "flags": 16, + "position": 61 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "id", + "value": "id", + "keyword": null, + "type": 0, + "flags": 0, + "position": 62 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 64 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "=", + "value": "=", + "keyword": null, + "type": 2, + "flags": 2, + "position": 65 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 66 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "i", + "value": "i", + "keyword": null, + "type": 0, + "flags": 0, + "position": 67 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": ".", + "value": ".", + "keyword": null, + "type": 2, + "flags": 16, + "position": 68 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "id", + "value": "id", + "keyword": null, + "type": 0, + "flags": 0, + "position": 69 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": null, + "value": null, + "keyword": null, + "type": 9, + "flags": 0, + "position": null + } + ], + "count": 35, + "idx": 35 + }, + "delimiter": ";", + "delimiterLen": 1, + "strict": false, + "errors": [] + }, + "parser": { + "@type": "PhpMyAdmin\\SqlParser\\Parser", + "list": { + "@type": "@1" + }, + "statements": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Statements\\SelectStatement", + "expr": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Components\\Expression", + "database": null, + "table": null, + "column": null, + "expr": "COUNT(*)", + "alias": "amount", + "function": "COUNT", + "subquery": null + } + ], + "from": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Components\\Expression", + "database": null, + "table": "one", + "column": null, + "expr": "one", + "alias": "i", + "function": null, + "subquery": null + } + ], + "index_hints": null, + "partition": null, + "where": null, + "group": null, + "group_options": null, + "having": null, + "order": null, + "limit": null, + "procedure": null, + "into": null, + "join": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Components\\JoinKeyword", + "type": "JOIN", + "expr": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\Expression", + "database": null, + "table": "two", + "column": null, + "expr": "two", + "alias": "io", + "function": null, + "subquery": null + }, + "on": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Components\\Condition", + "identifiers": [ + "io", + "id", + "i" + ], + "isOperator": false, + "expr": "io.id = i.id" + } + ], + "using": null + } + ], + "union": [], + "end_options": null, + "options": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray", + "options": [] + }, + "first": 0, + "last": 33 + } + ], + "brackets": 0, + "strict": false, + "errors": [] + }, + "errors": { + "lexer": [], + "parser": [] + } +} \ No newline at end of file diff --git a/tests/data/parser/parseLockErr2.out b/tests/data/parser/parseLockErr2.out index dfd5d8edc..1db477b2b 100644 --- a/tests/data/parser/parseLockErr2.out +++ b/tests/data/parser/parseLockErr2.out @@ -162,6 +162,13 @@ "errors": { "lexer": [], "parser": [ + [ + "An alias was previously found.", + { + "@type": "@12" + }, + 0 + ], [ "Unexpected keyword.", { diff --git a/tests/data/parser/parseTransaction3.in b/tests/data/parser/parseTransaction3.in index bc72a704d..5e7ed78b9 100644 --- a/tests/data/parser/parseTransaction3.in +++ b/tests/data/parser/parseTransaction3.in @@ -1,3 +1,3 @@ begin; -SELECT * FROM `tablename` +SELECT * FROM `tablename`; commit; diff --git a/tests/data/parser/parseTransaction3.out b/tests/data/parser/parseTransaction3.out index e0bb18259..50f4e5de1 100644 --- a/tests/data/parser/parseTransaction3.out +++ b/tests/data/parser/parseTransaction3.out @@ -1,10 +1,10 @@ { - "query": "begin;\nSELECT * FROM `tablename`\ncommit;\n", + "query": "begin;\nSELECT * FROM `tablename`;\ncommit;\n", "lexer": { "@type": "PhpMyAdmin\\SqlParser\\Lexer", - "str": "begin;\nSELECT * FROM `tablename`\ncommit;\n", - "len": 41, - "last": 41, + "str": "begin;\nSELECT * FROM `tablename`;\ncommit;\n", + "len": 42, + "last": 42, "list": { "@type": "PhpMyAdmin\\SqlParser\\TokensList", "tokens": [ @@ -98,6 +98,15 @@ "flags": 2, "position": 21 }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": ";", + "value": ";", + "keyword": null, + "type": 9, + "flags": 0, + "position": 32 + }, { "@type": "PhpMyAdmin\\SqlParser\\Token", "token": "\n", @@ -105,7 +114,7 @@ "keyword": null, "type": 3, "flags": 0, - "position": 32 + "position": 33 }, { "@type": "PhpMyAdmin\\SqlParser\\Token", @@ -114,7 +123,7 @@ "keyword": "COMMIT", "type": 1, "flags": 1, - "position": 33 + "position": 34 }, { "@type": "PhpMyAdmin\\SqlParser\\Token", @@ -123,7 +132,7 @@ "keyword": null, "type": 9, "flags": 0, - "position": 39 + "position": 40 }, { "@type": "PhpMyAdmin\\SqlParser\\Token", @@ -132,7 +141,7 @@ "keyword": null, "type": 3, "flags": 0, - "position": 40 + "position": 41 }, { "@type": "PhpMyAdmin\\SqlParser\\Token", @@ -144,8 +153,8 @@ "position": null } ], - "count": 15, - "idx": 15 + "count": 16, + "idx": 16 }, "delimiter": ";", "delimiterLen": 1, @@ -202,7 +211,7 @@ "options": [] }, "first": 1, - "last": 10 + "last": 9 } ], "brackets": 0, @@ -211,14 +220,6 @@ }, "errors": { "lexer": [], - "parser": [ - [ - "A new statement was found, but no delimiter between it and the previous one.", - { - "@type": "@13" - }, - 0 - ] - ] + "parser": [] } } \ No newline at end of file