diff --git a/clickhouse_sqlalchemy/alembic/comparators.py b/clickhouse_sqlalchemy/alembic/comparators.py index d42e495f..5e9f8b08 100644 --- a/clickhouse_sqlalchemy/alembic/comparators.py +++ b/clickhouse_sqlalchemy/alembic/comparators.py @@ -1,9 +1,10 @@ import logging +from alembic import __version__ as alembic_version from alembic.autogenerate import comparators from alembic.autogenerate.compare import _compare_columns from alembic.operations.ops import ModifyTableOps -from alembic.util.sqla_compat import _reflect_table +from alembic.util.sqla_compat import _reflect_table as _alembic_reflect_table from sqlalchemy import schema as sa_schema from sqlalchemy import text @@ -12,6 +13,10 @@ logger = logging.getLogger(__name__) +alembic_version = tuple( + (int(x) if x.isdigit() else x) for x in alembic_version.split('.') +) + def _extract_to_table_name(create_table_query): query = create_table_query @@ -29,6 +34,13 @@ def _extract_to_table_name(create_table_query): comparators.dispatch_for('schema', 'clickhouse')(default_comparator) +def _reflect_table(inspector, table): + if alembic_version >= (1, 11, 0): + return _alembic_reflect_table(inspector, table) + else: + return _alembic_reflect_table(inspector, table) + + @comparators.dispatch_for('schema', 'clickhouse') def compare_mat_view(autogen_context, upgrade_ops, schemas): connection = autogen_context.connection @@ -115,7 +127,7 @@ def compare_mat_view(autogen_context, upgrade_ops, schemas): ) else: table = Table(name, existing_metadata) - _reflect_table(inspector, table, None) + _reflect_table(inspector, table) drop = operations.DropMatViewOp( name, params.as_select, params.engine_full, *table.columns @@ -132,7 +144,7 @@ def compare_mat_view(autogen_context, upgrade_ops, schemas): inner_name = '.inner.' + name conn_table = Table(inner_name, existing_metadata) - _reflect_table(inspector, conn_table, None) + _reflect_table(inspector, conn_table) if not autogen_context.run_object_filters( view, name, 'mat_view', False, conn_table diff --git a/clickhouse_sqlalchemy/drivers/http/escaper.py b/clickhouse_sqlalchemy/drivers/http/escaper.py index 10ebf991..ab259f10 100644 --- a/clickhouse_sqlalchemy/drivers/http/escaper.py +++ b/clickhouse_sqlalchemy/drivers/http/escaper.py @@ -66,6 +66,13 @@ def escape_item(self, item): return "[" + ", ".join( [str(self.escape_item(x)) for x in item] ) + "]" + elif isinstance(item, dict): + return "{" + ", ".join( + ["{}: {}".format( + self.escape_item(k), + self.escape_item(v) + ) for k, v in item.items()] + ) + "}" elif isinstance(item, enum.Enum): return self.escape_string(item.name) else: diff --git a/tests/drivers/http/test_escaping.py b/tests/drivers/http/test_escaping.py index 0f549d2c..a456ecc9 100644 --- a/tests/drivers/http/test_escaping.py +++ b/tests/drivers/http/test_escaping.py @@ -28,7 +28,7 @@ def test_escaper(self): self.assertEqual(e.escape([Decimal('10')]), '[10.0]') self.assertEqual(e.escape([10.0]), '[10.0]') self.assertEqual(e.escape([date(2017, 1, 2)]), "['2017-01-02']") - + self.assertEqual(e.escape(dict(x=10, y=20)), {'x': 10, 'y': 20}) with self.assertRaises(Exception) as ex: e.escape([object()]) diff --git a/tests/sql/test_insert.py b/tests/sql/test_insert.py index 70b44532..26d2fdc1 100644 --- a/tests/sql/test_insert.py +++ b/tests/sql/test_insert.py @@ -20,3 +20,22 @@ def test_insert(self): rv = self.session.execute(select(table.c.x)).scalar() self.assertEqual(rv, 'test') + + @require_server_version(21, 1, 3) + def test_insert_map(self): + table = Table( + 't', self.metadata(), + Column('x', types.Map(types.String, types.Int32), + primary_key=True), + engines.Memory() + ) + + with self.create_table(table): + dict_map = dict(key1=1, Key2=2) + x = [ + {'x': dict_map} + ] + self.session.execute(table.insert(), x) + + rv = self.session.execute(select(table.c.x)).scalar() + self.assertEqual(rv, dict_map)