Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(type): support dynamic column #331

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clickhouse_sqlalchemy/drivers/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
'Enum8': types.Enum8,
'Enum16': types.Enum16,
'Object(\'json\')': types.JSON,
'Dynamic': types.Dynamic,
'_array': types.Array,
'_nullable': types.Nullable,
'_lowcardinality': types.LowCardinality,
Expand Down
3 changes: 3 additions & 0 deletions clickhouse_sqlalchemy/drivers/compilers/typecompiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ def visit_boolean(self, type_, **kw):
def visit_json(self, type_, **kw):
return 'JSON'

def visit_dynamic(self, type_, **kw):
return 'Dynamic'

def visit_nested(self, nested, **kwargs):
ddl_compiler = self.dialect.ddl_compiler(self.dialect, None)
cols_create = [
Expand Down
2 changes: 2 additions & 0 deletions clickhouse_sqlalchemy/types/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
'IPv4',
'IPv6',
'JSON',
'Dynamic',
'Nested',
'Tuple',
'Map',
Expand Down Expand Up @@ -70,6 +71,7 @@
from .common import Enum16
from .common import Decimal
from .common import JSON
from .common import Dynamic
from .common import Tuple
from .common import Map
from .common import AggregateFunction
Expand Down
3 changes: 3 additions & 0 deletions clickhouse_sqlalchemy/types/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ class Boolean(types.Boolean, ClickHouseTypeEngine):
class JSON(types.JSON, ClickHouseTypeEngine):
__visit_name__ = 'json'

class Dynamic(types.Dynamic, ClickHouseTypeEngine):
pass


class Array(ClickHouseTypeEngine):
__visit_name__ = 'array'
Expand Down
61 changes: 61 additions & 0 deletions tests/types/test_dynamic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import Dynamic
from sqlalchemy import Column, text, inspect, func
from sqlalchemy.sql.ddl import CreateTable

from clickhouse_sqlalchemy import types, engines, Table
from tests.testcase import BaseTestCase, CompilationTestCase
from tests.util import class_name_func
from parameterized import parameterized_class
from tests.session import native_session


class DynamicCompilationTestCase(CompilationTestCase):
def test_create_table(self):
table = Table(
'test', CompilationTestCase.metadata(),
Column('x', types.Dynamic),
engines.Memory()
)

self.assertEqual(
self.compile(CreateTable(table)),
'CREATE TABLE test (x Dynamic) ENGINE = Memory'
)


@parameterized_class(
[{'session': native_session}],
class_name_func=class_name_func
)
class DynamicTestCase(BaseTestCase):
required_server_version = (24, 5, 1)

table = Table(
'test', BaseTestCase.metadata(),
Column('x', types.Dynamic),
engines.Memory()
)

def test_select_insert(self):
data = {'k1': 1, 'k2': '2', 'k3': True}

self.table.drop(bind=self.session.bind, if_exists=True)
try:
# http session is unsupport
self.session.execute(
text('SET allow_experimental_object_type = 1;')
)
self.session.execute(text(self.compile(CreateTable(self.table))))
self.session.execute(self.table.insert(), [{'x': data}])
coltype = inspect(self.session.bind).get_columns('test')[0]['type']
self.assertIsInstance(coltype, types.Dynamic)
# https://clickhouse.com/docs/en/sql-reference/functions/Dynamic-functions#toDynamicstring
# The Dynamic type returns a tuple of values by default,
# which needs to be converted to Dynamic using the
# toDynamicString function.
res = self.session.query(
func.toDynamicString(self.table.c.x)
).scalar()
self.assertEqual(Dynamic.loads(res), data)
finally:
self.table.drop(bind=self.session.bind, if_exists=True)
Loading