diff --git a/src/Components/AlterOperation.php b/src/Components/AlterOperation.php index 185629454..6e2d9b370 100644 --- a/src/Components/AlterOperation.php +++ b/src/Components/AlterOperation.php @@ -13,6 +13,7 @@ use function in_array; use function is_numeric; use function is_string; +use function trim; /** * Parses an alter operation. @@ -381,6 +382,13 @@ public static function parse(Parser $parser, TokensList $list, array $options = --$list->idx; } + // If the operation is a RENAME COLUMN, now we have detected the field to rename, we need to parse + // again the options to get the new name of the column. + if ($ret->options->has('RENAME') && $ret->options->has('COLUMN')) { + $nextOptions = OptionsArray::parse($parser, $list, $options); + $ret->options->merge($nextOptions); + } + $state = 2; } elseif ($state === 2) { if (is_string($token->value) || is_numeric($token->value)) { @@ -485,18 +493,27 @@ public static function parse(Parser $parser, TokensList $list, array $options = */ public static function build($component, array $options = []) { + // Specific case of RENAME COLUMN that insert the field between 2 options. + $afterFieldsOptions = new OptionsArray(); + if ($component->options->has('RENAME') && $component->options->has('COLUMN')) { + $afterFieldsOptions = clone $component->options; + $afterFieldsOptions->remove('RENAME'); + $afterFieldsOptions->remove('COLUMN'); + $component->options->remove('TO'); + } + $ret = $component->options . ' '; if (isset($component->field) && ($component->field !== '')) { $ret .= $component->field . ' '; } - $ret .= TokensList::build($component->unknown); + $ret .= $afterFieldsOptions . TokensList::build($component->unknown); if (isset($component->partitions)) { $ret .= PartitionDefinition::build($component->partitions); } - return $ret; + return trim($ret); } /** diff --git a/tests/Builder/AlterStatementTest.php b/tests/Builder/AlterStatementTest.php index 69ed556a5..56b4efe21 100644 --- a/tests/Builder/AlterStatementTest.php +++ b/tests/Builder/AlterStatementTest.php @@ -4,6 +4,7 @@ namespace PhpMyAdmin\SqlParser\Tests\Builder; +use Generator; use PhpMyAdmin\SqlParser\Parser; use PhpMyAdmin\SqlParser\Tests\TestCase; @@ -113,4 +114,44 @@ public function testBuilderEventWithDefiner(): void $stmt = $parser->statements[0]; $this->assertEquals($query, $stmt->build()); } + + /** + * @return Generator + */ + public static function provideBuilderForRenameColumn(): Generator + { + $query = 'ALTER TABLE myTable RENAME COLUMN a TO b'; + + yield 'Single RENAME COLUMN' => [$query]; + + $query = 'ALTER TABLE myTable RENAME COLUMN a TO b, RENAME COLUMN b TO a'; + + yield 'Multiple RENAME COLUMN' => [$query]; + + $query = 'ALTER TABLE myTable ' . + 'RENAME COLUMN a TO b, ' . + 'RENAME COLUMN b TO a, ' . + 'RENAME INDEX oldIndex TO newIndex, ' . + 'RENAME TO newTable'; + + yield 'Mixed RENAME COLUMN + RENAME INDEX + RENAME table' => [$query]; + + $query = 'ALTER TABLE myTable ' . + 'RENAME TO newTable, ' . + 'RENAME INDEX oldIndex TO newIndex, ' . + 'RENAME COLUMN b TO a, ' . + 'RENAME COLUMN a TO b'; + + yield 'Mixed RENAME table + RENAME INDEX + RENAME COLUMNS' => [$query]; + } + + /** + * @dataProvider provideBuilderForRenameColumn + */ + public function testBuilderRenameColumn(string $query): void + { + $parser = new Parser($query); + $stmt = $parser->statements[0]; + $this->assertEquals($query, $stmt->build()); + } } diff --git a/tests/Parser/AlterStatementTest.php b/tests/Parser/AlterStatementTest.php index 267f16484..a793169e3 100644 --- a/tests/Parser/AlterStatementTest.php +++ b/tests/Parser/AlterStatementTest.php @@ -79,6 +79,8 @@ public function alterProvider(): array ['parser/parseAlterEventOnScheduleEvery6'], ['parser/parseAlterEventWithDefiner'], ['parser/parseAlterEventWithOtherDefiners'], + ['parser/parseAlterRenameColumn'], + ['parser/parseAlterRenameColumns'], ]; } } diff --git a/tests/data/parser/parseAlterRenameColumn.in b/tests/data/parser/parseAlterRenameColumn.in new file mode 100644 index 000000000..dd90c1490 --- /dev/null +++ b/tests/data/parser/parseAlterRenameColumn.in @@ -0,0 +1 @@ +ALTER TABLE myTable RENAME COLUMN foo TO bar; diff --git a/tests/data/parser/parseAlterRenameColumn.out b/tests/data/parser/parseAlterRenameColumn.out new file mode 100644 index 000000000..9023381cb --- /dev/null +++ b/tests/data/parser/parseAlterRenameColumn.out @@ -0,0 +1,248 @@ +{ + "query": "ALTER TABLE myTable RENAME COLUMN foo TO bar;\n", + "lexer": { + "@type": "PhpMyAdmin\\SqlParser\\Lexer", + "str": "ALTER TABLE myTable RENAME COLUMN foo TO bar;\n", + "len": 46, + "last": 46, + "list": { + "@type": "PhpMyAdmin\\SqlParser\\TokensList", + "tokens": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "ALTER", + "value": "ALTER", + "keyword": "ALTER", + "type": 1, + "flags": 3, + "position": 0 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 5 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "TABLE", + "value": "TABLE", + "keyword": "TABLE", + "type": 1, + "flags": 3, + "position": 6 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 11 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "myTable", + "value": "myTable", + "keyword": null, + "type": 0, + "flags": 0, + "position": 12 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 19 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "RENAME", + "value": "RENAME", + "keyword": "RENAME", + "type": 1, + "flags": 3, + "position": 20 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 26 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "COLUMN", + "value": "COLUMN", + "keyword": "COLUMN", + "type": 1, + "flags": 3, + "position": 27 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 33 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "foo", + "value": "foo", + "keyword": null, + "type": 0, + "flags": 0, + "position": 34 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 37 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "TO", + "value": "TO", + "keyword": "TO", + "type": 1, + "flags": 3, + "position": 38 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 40 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "bar", + "value": "bar", + "keyword": null, + "type": 0, + "flags": 0, + "position": 41 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": ";", + "value": ";", + "keyword": null, + "type": 9, + "flags": 0, + "position": 44 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "\n", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 45 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": null, + "value": null, + "keyword": null, + "type": 9, + "flags": 0, + "position": null + } + ], + "count": 18, + "idx": 18 + }, + "delimiter": ";", + "delimiterLen": 1, + "strict": false, + "errors": [] + }, + "parser": { + "@type": "PhpMyAdmin\\SqlParser\\Parser", + "list": { + "@type": "@1" + }, + "statements": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Statements\\AlterStatement", + "table": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\Expression", + "database": null, + "table": "myTable", + "column": null, + "expr": "myTable", + "alias": null, + "function": null, + "subquery": null + }, + "altered": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Components\\AlterOperation", + "options": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray", + "options": [ + "RENAME", + "COLUMN", + { + "name": "TO", + "equals": false, + "expr": "bar", + "value": "bar" + } + ] + }, + "field": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\Expression", + "database": null, + "table": null, + "column": "foo", + "expr": "foo", + "alias": null, + "function": null, + "subquery": null + }, + "partitions": null, + "unknown": [] + } + ], + "options": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray", + "options": { + "3": "TABLE" + } + }, + "first": 0, + "last": 15 + } + ], + "brackets": 0, + "strict": false, + "errors": [] + }, + "errors": { + "lexer": [], + "parser": [] + } +} \ No newline at end of file diff --git a/tests/data/parser/parseAlterRenameColumns.in b/tests/data/parser/parseAlterRenameColumns.in new file mode 100644 index 000000000..e2ca53008 --- /dev/null +++ b/tests/data/parser/parseAlterRenameColumns.in @@ -0,0 +1,4 @@ +ALTER TABLE myTable RENAME COLUMN a TO b, + RENAME COLUMN b TO c, + RENAME COLUMN c TO d, + RENAME COLUMN d TO a; diff --git a/tests/data/parser/parseAlterRenameColumns.out b/tests/data/parser/parseAlterRenameColumns.out new file mode 100644 index 000000000..bd12e0e7a --- /dev/null +++ b/tests/data/parser/parseAlterRenameColumns.out @@ -0,0 +1,629 @@ +{ + "query": "ALTER TABLE myTable RENAME COLUMN a TO b,\n RENAME COLUMN b TO c,\n RENAME COLUMN c TO d,\n RENAME COLUMN d TO a;\n", + "lexer": { + "@type": "PhpMyAdmin\\SqlParser\\Lexer", + "str": "ALTER TABLE myTable RENAME COLUMN a TO b,\n RENAME COLUMN b TO c,\n RENAME COLUMN c TO d,\n RENAME COLUMN d TO a;\n", + "len": 168, + "last": 168, + "list": { + "@type": "PhpMyAdmin\\SqlParser\\TokensList", + "tokens": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "ALTER", + "value": "ALTER", + "keyword": "ALTER", + "type": 1, + "flags": 3, + "position": 0 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 5 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "TABLE", + "value": "TABLE", + "keyword": "TABLE", + "type": 1, + "flags": 3, + "position": 6 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 11 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "myTable", + "value": "myTable", + "keyword": null, + "type": 0, + "flags": 0, + "position": 12 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 19 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "RENAME", + "value": "RENAME", + "keyword": "RENAME", + "type": 1, + "flags": 3, + "position": 20 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 26 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "COLUMN", + "value": "COLUMN", + "keyword": "COLUMN", + "type": 1, + "flags": 3, + "position": 27 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 33 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "a", + "value": "a", + "keyword": null, + "type": 0, + "flags": 0, + "position": 34 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 35 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "TO", + "value": "TO", + "keyword": "TO", + "type": 1, + "flags": 3, + "position": 36 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 38 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "b", + "value": "b", + "keyword": null, + "type": 0, + "flags": 0, + "position": 39 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": ",", + "value": ",", + "keyword": null, + "type": 2, + "flags": 16, + "position": 40 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "\n ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 41 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "RENAME", + "value": "RENAME", + "keyword": "RENAME", + "type": 1, + "flags": 3, + "position": 62 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 68 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "COLUMN", + "value": "COLUMN", + "keyword": "COLUMN", + "type": 1, + "flags": 3, + "position": 69 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 75 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "b", + "value": "b", + "keyword": null, + "type": 0, + "flags": 0, + "position": 76 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 77 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "TO", + "value": "TO", + "keyword": "TO", + "type": 1, + "flags": 3, + "position": 78 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 80 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "c", + "value": "c", + "keyword": null, + "type": 0, + "flags": 0, + "position": 81 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": ",", + "value": ",", + "keyword": null, + "type": 2, + "flags": 16, + "position": 82 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "\n ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 83 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "RENAME", + "value": "RENAME", + "keyword": "RENAME", + "type": 1, + "flags": 3, + "position": 104 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 110 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "COLUMN", + "value": "COLUMN", + "keyword": "COLUMN", + "type": 1, + "flags": 3, + "position": 111 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 117 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "c", + "value": "c", + "keyword": null, + "type": 0, + "flags": 0, + "position": 118 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 119 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "TO", + "value": "TO", + "keyword": "TO", + "type": 1, + "flags": 3, + "position": 120 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 122 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "d", + "value": "d", + "keyword": null, + "type": 0, + "flags": 0, + "position": 123 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": ",", + "value": ",", + "keyword": null, + "type": 2, + "flags": 16, + "position": 124 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "\n ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 125 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "RENAME", + "value": "RENAME", + "keyword": "RENAME", + "type": 1, + "flags": 3, + "position": 146 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 152 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "COLUMN", + "value": "COLUMN", + "keyword": "COLUMN", + "type": 1, + "flags": 3, + "position": 153 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 159 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "d", + "value": "d", + "keyword": null, + "type": 0, + "flags": 0, + "position": 160 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 161 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "TO", + "value": "TO", + "keyword": "TO", + "type": 1, + "flags": 3, + "position": 162 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": " ", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 164 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "a", + "value": "a", + "keyword": null, + "type": 0, + "flags": 0, + "position": 165 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": ";", + "value": ";", + "keyword": null, + "type": 9, + "flags": 0, + "position": 166 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": "\n", + "value": " ", + "keyword": null, + "type": 3, + "flags": 0, + "position": 167 + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Token", + "token": null, + "value": null, + "keyword": null, + "type": 9, + "flags": 0, + "position": null + } + ], + "count": 51, + "idx": 51 + }, + "delimiter": ";", + "delimiterLen": 1, + "strict": false, + "errors": [] + }, + "parser": { + "@type": "PhpMyAdmin\\SqlParser\\Parser", + "list": { + "@type": "@1" + }, + "statements": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Statements\\AlterStatement", + "table": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\Expression", + "database": null, + "table": "myTable", + "column": null, + "expr": "myTable", + "alias": null, + "function": null, + "subquery": null + }, + "altered": [ + { + "@type": "PhpMyAdmin\\SqlParser\\Components\\AlterOperation", + "options": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray", + "options": [ + "RENAME", + "COLUMN", + { + "name": "TO", + "equals": false, + "expr": "b", + "value": "b" + } + ] + }, + "field": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\Expression", + "database": null, + "table": null, + "column": "a", + "expr": "a", + "alias": null, + "function": null, + "subquery": null + }, + "partitions": null, + "unknown": [] + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Components\\AlterOperation", + "options": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray", + "options": [ + "RENAME", + "COLUMN", + { + "name": "TO", + "equals": false, + "expr": "c", + "value": "c" + } + ] + }, + "field": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\Expression", + "database": null, + "table": null, + "column": "b", + "expr": "b", + "alias": null, + "function": null, + "subquery": null + }, + "partitions": null, + "unknown": [] + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Components\\AlterOperation", + "options": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray", + "options": [ + "RENAME", + "COLUMN", + { + "name": "TO", + "equals": false, + "expr": "d", + "value": "d" + } + ] + }, + "field": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\Expression", + "database": null, + "table": null, + "column": "c", + "expr": "c", + "alias": null, + "function": null, + "subquery": null + }, + "partitions": null, + "unknown": [] + }, + { + "@type": "PhpMyAdmin\\SqlParser\\Components\\AlterOperation", + "options": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray", + "options": [ + "RENAME", + "COLUMN", + { + "name": "TO", + "equals": false, + "expr": "a", + "value": "a" + } + ] + }, + "field": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\Expression", + "database": null, + "table": null, + "column": "d", + "expr": "d", + "alias": null, + "function": null, + "subquery": null + }, + "partitions": null, + "unknown": [] + } + ], + "options": { + "@type": "PhpMyAdmin\\SqlParser\\Components\\OptionsArray", + "options": { + "3": "TABLE" + } + }, + "first": 0, + "last": 48 + } + ], + "brackets": 0, + "strict": false, + "errors": [] + }, + "errors": { + "lexer": [], + "parser": [] + } +} \ No newline at end of file