diff --git a/CHANGELOG b/CHANGELOG index edfedb4f..570cc618 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -33,6 +33,9 @@ Bug Fixes * Fix statement splitting (issue845). * Fix a late-binding closure bug in `TokenList.token_not_matching`. +* Recognize ``ROW_FORMAT`` as a keyword so that ``ALTER TABLE ... ROW_FORMAT=...`` + no longer merges the table name and the option into a single identifier + (issue773). Release 0.5.5 (Dec 19, 2025) diff --git a/sqlparse/keywords.py b/sqlparse/keywords.py index 4bafcda1..12acb84f 100644 --- a/sqlparse/keywords.py +++ b/sqlparse/keywords.py @@ -488,6 +488,7 @@ 'ROUTINE_SCHEMA': tokens.Keyword, 'ROWS': tokens.Keyword, 'ROW_COUNT': tokens.Keyword, + 'ROW_FORMAT': tokens.Keyword, 'RULE': tokens.Keyword, 'SAVE_POINT': tokens.Keyword, diff --git a/tests/test_regressions.py b/tests/test_regressions.py index 15ac9ee9..5be4ec42 100644 --- a/tests/test_regressions.py +++ b/tests/test_regressions.py @@ -454,6 +454,21 @@ def test_primary_key_issue740(): assert p.tokens[0].ttype == T.Keyword +def test_alter_table_row_format_issue773(): + # ROW_FORMAT is a keyword (like ENGINE), so it must not be absorbed into + # the preceding table name as if it were an alias. + p = sqlparse.parse('ALTER TABLE mytable ROW_FORMAT=Dynamic')[0] + row_format = p.token_next_by(m=(T.Keyword, 'ROW_FORMAT'))[1] + assert row_format is not None + # The table name should be parsed as a standalone identifier. + assert isinstance(p.tokens[4], sql.Identifier) + assert p.tokens[4].get_real_name() == 'mytable' + assert p.tokens[4].get_alias() is None + # Sanity check: ENGINE (already a keyword) behaves the same way. + e = sqlparse.parse('ALTER TABLE mytable ENGINE=InnoDB')[0] + assert e.tokens[4].get_real_name() == 'mytable' + + @pytest.fixture def limit_recursion(): curr_limit = sys.getrecursionlimit()