diff --git a/clickhouse_sqlalchemy/drivers/base.py b/clickhouse_sqlalchemy/drivers/base.py index 7e102df1..075101df 100644 --- a/clickhouse_sqlalchemy/drivers/base.py +++ b/clickhouse_sqlalchemy/drivers/base.py @@ -347,7 +347,16 @@ def _parse_detetime_params(spec): @staticmethod def _parse_options(option_string): - options = dict() + def _fix_invalid_enum_option_name(option_name): + # Python Enum does not support blank string ("") + # or "mro" as option name + if option_name == '': + option_name = '_' + elif option_name == 'mro': + option_name = '__' + return option_name + + options = {} after_name = False escaped = False quote_character = None @@ -363,6 +372,7 @@ def _parse_options(option_string): if ch in (' ', '='): pass elif ch == ',': + name = _fix_invalid_enum_option_name(name) options[name] = int(value) after_name = False name = '' @@ -384,6 +394,7 @@ def _parse_options(option_string): quote_character = ch if after_name: + name = _fix_invalid_enum_option_name(name) options.setdefault(name, int(value)) # Word after last comma return options diff --git a/tests/drivers/test_clickhouse_dialect.py b/tests/drivers/test_clickhouse_dialect.py index f7c439e2..3c617a8a 100644 --- a/tests/drivers/test_clickhouse_dialect.py +++ b/tests/drivers/test_clickhouse_dialect.py @@ -141,6 +141,21 @@ def test_empty_set_expr(self): self.assertEqual(len(rv), 0) + def test_enum_type_with_illegal_characters(self): + empty_string = '' + mro = 'mro' + try: + self.dialect._get_column_type( + 'col_name', + f"Enum8('f' = -1, '{empty_string}' = 0, 'ok' = 1, '{mro}' = 2)" + ) + self.dialect._get_column_type( + 'col_name', + f"Enum8('f' = -1, '{mro}' = 0, 'ok' = 1, '{empty_string}' = 2)" + ) + except ValueError as e: + self.fail(f"Enum options parsing failed: {e}") + class ClickHouseAsynchDialectTestCase(BaseAsynchTestCase):