From deafe191331b6e7b087b74b0c8103d4e5bb696e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=B8=D1=89=D1=83=D0=BA=20=D0=90=D0=BB?= =?UTF-8?q?=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Fri, 13 Sep 2024 11:11:03 +0300 Subject: [PATCH 1/5] Fix error for enums: empty string ("") or "mro" as option name --- clickhouse_sqlalchemy/drivers/base.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/clickhouse_sqlalchemy/drivers/base.py b/clickhouse_sqlalchemy/drivers/base.py index 7e102df1..52244920 100644 --- a/clickhouse_sqlalchemy/drivers/base.py +++ b/clickhouse_sqlalchemy/drivers/base.py @@ -363,6 +363,11 @@ def _parse_options(option_string): if ch in (' ', '='): pass elif ch == ',': + # Python Enum does not support empty ("") string or "mro" as name + if name == '': + name = '_' + elif name == 'mro': + name = '__' options[name] = int(value) after_name = False name = '' From 823a8bccaf15c260bfa8b5413c6806acd7eebd1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=B8=D1=89=D1=83=D0=BA=20=D0=90=D0=BB?= =?UTF-8?q?=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Fri, 13 Sep 2024 12:00:37 +0300 Subject: [PATCH 2/5] added tests and refactor --- clickhouse_sqlalchemy/drivers/base.py | 18 ++++++++++++------ tests/drivers/test_clickhouse_dialect.py | 9 +++++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/clickhouse_sqlalchemy/drivers/base.py b/clickhouse_sqlalchemy/drivers/base.py index 52244920..fa3ffee2 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_member_name(member_name): + # Python Enum does not support blank string ("") + # or "mro" as option name + if member_name == '': + member_name = '_' + elif member_name == 'mro': + member_name = '__' + return member_name + + options = {} after_name = False escaped = False quote_character = None @@ -363,11 +372,7 @@ def _parse_options(option_string): if ch in (' ', '='): pass elif ch == ',': - # Python Enum does not support empty ("") string or "mro" as name - if name == '': - name = '_' - elif name == 'mro': - name = '__' + name = _fix_invalid_enum_member_name(name) options[name] = int(value) after_name = False name = '' @@ -389,6 +394,7 @@ def _parse_options(option_string): quote_character = ch if after_name: + name = _fix_invalid_enum_member_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..9af4cc3d 100644 --- a/tests/drivers/test_clickhouse_dialect.py +++ b/tests/drivers/test_clickhouse_dialect.py @@ -141,6 +141,15 @@ 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('unknown' = -1, '{empty_string}' = 0, 'known' = 1, '{mro}' = 2)") + self.dialect._get_column_type('col_name', f"Enum8('unknown' = -1, '{mro}' = 0, 'known' = 1, '{empty_string}' = 2)") + except ValueError as e: + self.fail(f"Enum options parsing failed: {e}") + class ClickHouseAsynchDialectTestCase(BaseAsynchTestCase): From d5efe6592790db288da819da63535793d786ce60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=B8=D1=89=D1=83=D0=BA=20=D0=90=D0=BB?= =?UTF-8?q?=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Fri, 13 Sep 2024 12:02:14 +0300 Subject: [PATCH 3/5] doc refactor --- clickhouse_sqlalchemy/drivers/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clickhouse_sqlalchemy/drivers/base.py b/clickhouse_sqlalchemy/drivers/base.py index fa3ffee2..4aece770 100644 --- a/clickhouse_sqlalchemy/drivers/base.py +++ b/clickhouse_sqlalchemy/drivers/base.py @@ -349,7 +349,7 @@ def _parse_detetime_params(spec): def _parse_options(option_string): def _fix_invalid_enum_member_name(member_name): # Python Enum does not support blank string ("") - # or "mro" as option name + # or "mro" as member name if member_name == '': member_name = '_' elif member_name == 'mro': From 084c80902f24fe20999f97185a6d6f3758912dc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=B8=D1=89=D1=83=D0=BA=20=D0=90=D0=BB?= =?UTF-8?q?=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Fri, 13 Sep 2024 12:03:54 +0300 Subject: [PATCH 4/5] deal with naming --- clickhouse_sqlalchemy/drivers/base.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/clickhouse_sqlalchemy/drivers/base.py b/clickhouse_sqlalchemy/drivers/base.py index 4aece770..075101df 100644 --- a/clickhouse_sqlalchemy/drivers/base.py +++ b/clickhouse_sqlalchemy/drivers/base.py @@ -347,14 +347,14 @@ def _parse_detetime_params(spec): @staticmethod def _parse_options(option_string): - def _fix_invalid_enum_member_name(member_name): + def _fix_invalid_enum_option_name(option_name): # Python Enum does not support blank string ("") - # or "mro" as member name - if member_name == '': - member_name = '_' - elif member_name == 'mro': - member_name = '__' - return member_name + # or "mro" as option name + if option_name == '': + option_name = '_' + elif option_name == 'mro': + option_name = '__' + return option_name options = {} after_name = False @@ -372,7 +372,7 @@ def _fix_invalid_enum_member_name(member_name): if ch in (' ', '='): pass elif ch == ',': - name = _fix_invalid_enum_member_name(name) + name = _fix_invalid_enum_option_name(name) options[name] = int(value) after_name = False name = '' @@ -394,7 +394,7 @@ def _fix_invalid_enum_member_name(member_name): quote_character = ch if after_name: - name = _fix_invalid_enum_member_name(name) + name = _fix_invalid_enum_option_name(name) options.setdefault(name, int(value)) # Word after last comma return options From 580a0abfeb7ff1ec3ef98c6d0ecda7126b202c4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=B8=D1=89=D1=83=D0=BA=20=D0=90=D0=BB?= =?UTF-8?q?=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80?= Date: Fri, 13 Sep 2024 13:24:27 +0300 Subject: [PATCH 5/5] linter fix --- tests/drivers/test_clickhouse_dialect.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/drivers/test_clickhouse_dialect.py b/tests/drivers/test_clickhouse_dialect.py index 9af4cc3d..3c617a8a 100644 --- a/tests/drivers/test_clickhouse_dialect.py +++ b/tests/drivers/test_clickhouse_dialect.py @@ -145,8 +145,14 @@ def test_enum_type_with_illegal_characters(self): empty_string = '' mro = 'mro' try: - self.dialect._get_column_type('col_name', f"Enum8('unknown' = -1, '{empty_string}' = 0, 'known' = 1, '{mro}' = 2)") - self.dialect._get_column_type('col_name', f"Enum8('unknown' = -1, '{mro}' = 0, 'known' = 1, '{empty_string}' = 2)") + 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}")