diff --git a/CHANGELOG.md b/CHANGELOG.md index 7cc2ebda..bdb00943 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,10 @@ In any case, this should not affect the basic usage of Superset with ClickHouse. your Superset installation, the ClickHouse datasource will be available with either the enhanced connection dialog or a standard SqlAlchemy DSN in the form of `clickhousedb://{username}:{password}@{host}:{port}`. +## 0.6.21, 2023-11-22 +### New Feature +- Added support for Point type. Closes https://github.com/ClickHouse/clickhouse-connect/issues/151 + ## 0.6.20, 2023-11-09 ### Bug Fix - Fixed an issue where client side binding of datetimes with timezones would produce the incorrect time string if diff --git a/clickhouse_connect/datatypes/container.py b/clickhouse_connect/datatypes/container.py index 36e4c237..445b2414 100644 --- a/clickhouse_connect/datatypes/container.py +++ b/clickhouse_connect/datatypes/container.py @@ -154,6 +154,13 @@ def convert_dict_insert(self, column: Sequence) -> Sequence: return col +class Point(Tuple): + + def __init__(self, type_def): + super().__init__(type_def) + self._name_suffix = '' + + class Map(ClickHouseType): _slots = 'key_type', 'value_type' python_type = dict diff --git a/clickhouse_connect/datatypes/registry.py b/clickhouse_connect/datatypes/registry.py index 47da6e05..3544b377 100644 --- a/clickhouse_connect/datatypes/registry.py +++ b/clickhouse_connect/datatypes/registry.py @@ -35,6 +35,8 @@ def parse_name(name: str) -> Tuple[str, str, TypeDef]: elif base.startswith('Tuple'): keys, values = parse_columns(base[5:]) base = 'Tuple' + elif base == 'Point': + values = ['Float64', 'Float64'] else: try: base, values, _ = parse_callable(base) diff --git a/tests/integration_tests/test_native.py b/tests/integration_tests/test_native.py index af506619..57192c0f 100644 --- a/tests/integration_tests/test_native.py +++ b/tests/integration_tests/test_native.py @@ -166,6 +166,17 @@ def test_tuple_inserts(test_client: Client, table_context: Callable): assert query_result[2] == query_result[3] +def test_point_inserts(test_client: Client, table_context: Callable): + with table_context('insert_point_test', ['key Int32', 'point Point']): + data = [[1, (3.55, 3.55)], [2, (4.55, 4.55)]] + result = test_client.insert('insert_point_test', data) + assert 2 == result.written_rows + + query_result = test_client.query('SELECT * FROM insert_point_test ORDER BY key').result_rows + assert query_result[0] == (1, (3.55, 3.55)) + assert query_result[1] == (2, (4.55, 4.55)) + + def test_agg_function(test_client: Client, table_context: Callable): with table_context('agg_func_test', ['key Int32', 'str SimpleAggregateFunction(any, String)', diff --git a/tests/unit_tests/test_driver/test_native_read.py b/tests/unit_tests/test_driver/test_native_read.py index 8ae404e7..575b6234 100644 --- a/tests/unit_tests/test_driver/test_native_read.py +++ b/tests/unit_tests/test_driver/test_native_read.py @@ -97,6 +97,15 @@ def test_ip(): assert tuple(python) == tuple(IPv4Address(ip) for ip in ips) +def test_point(): + points = ((3.22, 3.22),(5.22, 5.22),(4.22, 4.22)) + point_type = registry.get_from_name('Point') + dest = bytearray() + point_type.write_column(points, dest, BaseQueryContext()) + python = point_type.read_column(bytes_source(bytes(dest)), 3, QueryContext()) + assert tuple(python) == tuple(point for point in points) + + def test_nested(): result = parse_response (bytes_source(NESTED_BINARY)) check_result(result, [{'str1': 'one', 'int32': 5}, {'str1': 'two', 'int32': 55}], 2, 0) diff --git a/tests/unit_tests/test_driver/test_native_write.py b/tests/unit_tests/test_driver/test_native_write.py index d30950bb..023bf964 100644 --- a/tests/unit_tests/test_driver/test_native_write.py +++ b/tests/unit_tests/test_driver/test_native_write.py @@ -29,6 +29,11 @@ 7472 696e 6732 0773 7472 696e 6733 """ +POINT_OUTPUT = """ +0101 0576 616c 7565 0550 6f69 6e74 c3f5 +285c 8fc2 0940 c3f5 285c 8fc2 0940 +""" + STRING_ACCEPTS_BYTES_OUTPUT = """ 0101 0576 616c 7565 0653 7472 696e 6701 ff @@ -77,6 +82,14 @@ def test_tuple_three(): assert bytes(output) == bytes.fromhex(TUPLE_THREE_OUTPUT) +def test_point(): + data = [[(3.22, 3.22)]] + names = ['value'] + types = [get_from_name('Point')] + output = native_insert_block(data, names, types) + assert bytes(output) == bytes.fromhex(POINT_OUTPUT) + + def test_nested(): data = [([],), ([{'str1': 'three', 'int32': 5}, {'str1': 'five', 'int32': 77}],),