Repository: tobymao/sqlglot

Issue:
MySQL: Parsing ALTER TABLE ... MODIFY not parsed as exp.AlterTable
When parsing through a modification of existing column in MySQL dialect (datatype or not null constraint being set), I get an error message: `'ALTER TABLE test_table MODIFY COLUMN test_column LONGTEXT' contains unsupported syntax. Falling back to parsing as a 'Command'.`

This statement is found in the test suite of the dialect, so I'm not sure why it would parse as command. Expected result would be to return Expression of type AlterTable.

MySQL documentation: https://dev.mysql.com/doc/refman/8.0/en/alter-table.html

Happy to contribute a PR if someone confirms the source of problems, but since I found this case also in the test suite, I'm not sure if there's something wrong with the tests too.


Discussion:
Hi,

Thanks for reporting this issue. Regarding the test cases, I could find it under 2 places:
1. In `test_transpile.py::test_command_identity()`, which validates that the queries under it will be parsed as commands and will roundtrip (i.e parsed + generated) as-is
2. In `test_mysql.py::test_ddl()` which uses a `validate_identity()` call with 2 queries; This indicates that the first query with `ALTER COLUMN` is what the SQLGlot parser will read, and the second query with `MODIFY COLUMN` matches what the MySQL Generator should write. However, the opposite direction is not always true, i.e this does not mean that `MODIFY COLUMN` is parseable syntax.

As for the `MODIFY COLUMN` MySQL syntax, it should be a relatively straightforward addition.

Resolving patch:
diff --git a/sqlglot/dialects/mysql.py b/sqlglot/dialects/mysql.py
--- a/sqlglot/dialects/mysql.py
+++ b/sqlglot/dialects/mysql.py
@@ -408,6 +408,11 @@ class Parser(parser.Parser):
             "SPATIAL": lambda self: self._parse_index_constraint(kind="SPATIAL"),
         }
 
+        ALTER_PARSERS = {
+            **parser.Parser.ALTER_PARSERS,
+            "MODIFY": lambda self: self._parse_alter_table_alter(),
+        }
+
         SCHEMA_UNNAMED_CONSTRAINTS = {
             *parser.Parser.SCHEMA_UNNAMED_CONSTRAINTS,
             "FULLTEXT",
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -5695,10 +5695,11 @@ def _parse_alter_table_alter(self) -> exp.AlterColumn:
             return self.expression(exp.AlterColumn, this=column, comment=self._parse_string())
 
         self._match_text_seq("SET", "DATA")
+        self._match_text_seq("TYPE")
         return self.expression(
             exp.AlterColumn,
             this=column,
-            dtype=self._match_text_seq("TYPE") and self._parse_types(),
+            dtype=self._parse_types(),
             collate=self._match(TokenType.COLLATE) and self._parse_term(),
             using=self._match(TokenType.USING) and self._parse_conjunction(),
         )


Test patch:
diff --git a/tests/dialects/test_mysql.py b/tests/dialects/test_mysql.py
--- a/tests/dialects/test_mysql.py
+++ b/tests/dialects/test_mysql.py
@@ -85,6 +85,9 @@ def test_ddl(self):
             "ALTER TABLE test_table ALTER COLUMN test_column SET DATA TYPE LONGTEXT",
             "ALTER TABLE test_table MODIFY COLUMN test_column LONGTEXT",
         )
+        self.validate_identity(
+            "ALTER TABLE test_table MODIFY COLUMN test_column LONGTEXT",
+        )
         self.validate_identity(
             "CREATE TABLE t (c DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC",
             "CREATE TABLE t (c DATETIME DEFAULT CURRENT_TIMESTAMP() ON UPDATE CURRENT_TIMESTAMP()) DEFAULT CHARACTER SET=utf8 ROW_FORMAT=DYNAMIC",