mirror of
https://github.com/hanzo-ds/connect.git
synced 2026-10-30 20:31:24 +00:00
Imports read `datastore_connect`, distribution `datastore-connect`, so nothing has to import another product's name to talk to Hanzo Datastore. Cut from upstream 0.8.17 — the version this stack already ran — rather than from upstream HEAD, so it is a PURE RENAME with no behaviour change. Forking from HEAD would have dragged versions along with the name (connect alone jumped 0.8.17 -> 1.6.0, a major), which is a different and much riskier change wearing a rename's clothes. X-ClickHouse-* headers are untouched: they are wire protocol the server sends and expects, and a different token shape from the module name.
169 lines
6.9 KiB
Python
169 lines
6.9 KiB
Python
import datetime
|
|
from ipaddress import IPv4Address
|
|
from typing import Callable
|
|
from uuid import UUID
|
|
|
|
import pytest
|
|
|
|
from datastore_connect.datatypes.format import set_write_format
|
|
from datastore_connect.driver import Client
|
|
|
|
|
|
def type_available(test_client: Client, data_type: str):
|
|
if not test_client.get_client_setting(f'allow_experimental_{data_type}_type'):
|
|
pytest.skip(f'New {data_type.upper()} type not available in this version: {test_client.server_version}')
|
|
|
|
|
|
def test_variant(test_client: Client, table_context: Callable):
|
|
type_available(test_client, 'variant')
|
|
with table_context('basic_variants', [
|
|
'key Int32',
|
|
'v1 Variant(UInt64, String, Array(UInt64), UUID)',
|
|
'v2 Variant(IPv4, Decimal(10, 2))']):
|
|
data = [[1, 58322, None],
|
|
[2, 'a string', 55.2],
|
|
[3, 'bef56f14-0870-4f82-a35e-9a47eff45a5b', 777.25],
|
|
[4, [120, 250], '243.12.55.44']
|
|
]
|
|
test_client.insert('basic_variants', data)
|
|
result = test_client.query('SELECT * FROM basic_variants ORDER BY key').result_set
|
|
assert result[2][1] == UUID('bef56f14-0870-4f82-a35e-9a47eff45a5b')
|
|
assert result[2][2] == 777.25
|
|
assert result[3][1] == [120, 250]
|
|
assert result[3][2] == IPv4Address('243.12.55.44')
|
|
|
|
|
|
def test_nested_variant(test_client: Client, table_context: Callable):
|
|
type_available(test_client, 'variant')
|
|
with table_context('nested_variants', [
|
|
'key Int32',
|
|
'm1 Map(String, Variant(String, UInt128, Bool))',
|
|
't1 Tuple(Int64, Variant(Bool, String, Int32))',
|
|
'a1 Array(Array(Variant(String, DateTime, Float64)))',
|
|
]):
|
|
data = [[1,
|
|
{'k1': 'string1', 'k2': 34782477743, 'k3':True},
|
|
(-40, True),
|
|
(('str3', 53.732),),
|
|
],
|
|
[2,
|
|
{'k1': False, 'k2': 's3872', 'k3': 100},
|
|
(340283, 'str'),
|
|
(),
|
|
]
|
|
]
|
|
test_client.insert('nested_variants', data)
|
|
result = test_client.query('SELECT * FROM nested_variants ORDER BY key').result_set
|
|
assert result[0][1]['k1'] == 'string1'
|
|
assert result[0][1]['k2'] == 34782477743
|
|
assert result[0][2] == (-40, True)
|
|
assert result[0][3][0][1] == 53.732
|
|
assert result[1][1]['k3'] == 100
|
|
|
|
|
|
def test_dynamic_nested(test_client: Client, table_context: Callable):
|
|
type_available(test_client, 'dynamic')
|
|
with table_context('nested_dynamics', [
|
|
'm2 Map(String, Dynamic)'
|
|
], order_by='()'):
|
|
data = [({'k4': 'string8', 'k5': 5000},)]
|
|
test_client.insert('nested_dynamics', data)
|
|
result = test_client.query('SELECT * FROM nested_dynamics').result_set
|
|
assert result[0][0]['k5'] == '5000'
|
|
|
|
|
|
def test_dynamic(test_client: Client, table_context: Callable):
|
|
type_available(test_client, 'dynamic')
|
|
with table_context('basic_dynamic', [
|
|
'key UInt64',
|
|
'v1 Dynamic',
|
|
'v2 Dynamic']):
|
|
data = [[1, 58322, 15.5],
|
|
[3, 'bef56f14-0870-4f82-a35e-9a47eff45a5b', 777.25],
|
|
[2, 'a string', 55.2],
|
|
[4, [120, 250], 577.22]
|
|
]
|
|
test_client.insert('basic_dynamic', data)
|
|
result = test_client.query('SELECT * FROM basic_dynamic ORDER BY key').result_set
|
|
assert result[2][1] == 'bef56f14-0870-4f82-a35e-9a47eff45a5b'
|
|
assert result[3][1] == '[120, 250]'
|
|
assert result[2][2] == '777.25'
|
|
|
|
|
|
def test_basic_json(test_client: Client, table_context: Callable):
|
|
type_available(test_client, 'json')
|
|
with table_context('new_json_basic', [
|
|
'key Int32',
|
|
'value JSON',
|
|
"null_value JSON"
|
|
]):
|
|
jv3 = {'key3': 752, 'value.2': 'v2_rules', 'blank': None}
|
|
jv1 = {'key1': 337, 'value.2': 'vvvv', 'HKD@spéçiäl': 'Special K', 'blank': 'not_really_blank'}
|
|
njv2 = {'nk1': -302, 'nk2': {'sub1': 372, 'sub2': 'a string'}}
|
|
njv3 = {'nk1': 5832.44, 'nk2': {'sub1': 47788382, 'sub2': 'sub2val', 'sub3': 'sub3str', 'space key': 'spacey'}}
|
|
test_client.insert('new_json_basic', [
|
|
[5, jv1, None],
|
|
[20, None, njv2],
|
|
[25, jv3, njv3]])
|
|
|
|
result = test_client.query('SELECT * FROM new_json_basic ORDER BY key').result_set
|
|
json1 = result[0][1]
|
|
assert json1['HKD@spéçiäl'] == 'Special K'
|
|
assert 'key3' not in json1
|
|
json2 = result[1][2]
|
|
assert json2['nk1'] == -302.0
|
|
assert json2['nk2']['sub2'] == 'a string'
|
|
assert json2['nk2'].get('sub3') is None
|
|
json3 = result[2][1]
|
|
assert json3['value']['2'] == 'v2_rules'
|
|
assert 'blank' not in json3
|
|
assert 'key1' not in json3
|
|
assert json3['key3'] == 752
|
|
null_json3 = result[2][2]
|
|
assert null_json3['nk2']['space key'] == 'spacey'
|
|
|
|
set_write_format('JSON', 'string')
|
|
test_client.insert('new_json_basic', [[999, '{"key4": 283, "value.2": "str_value"}', '{"nk1":53}']])
|
|
result = test_client.query('SELECT value.key4, null_value.nk1 FROM new_json_basic ORDER BY key').result_set
|
|
assert result[3][0] == 283
|
|
assert result[3][1] == 53
|
|
|
|
|
|
def test_typed_json(test_client: Client, table_context: Callable):
|
|
type_available(test_client, 'json')
|
|
with table_context('new_json_typed', [
|
|
'key Int32',
|
|
'value JSON(max_dynamic_paths=150, `a.b` DateTime64(3), SKIP a.c)'
|
|
]):
|
|
v1 = '{"a":{"b":"2020-10-15T10:15:44.877", "c":"skip_me"}}'
|
|
test_client.insert('new_json_typed', [[1, v1]])
|
|
result = test_client.query('SELECT * FROM new_json_typed ORDER BY key')
|
|
json1 = result.result_set[0][1]
|
|
assert json1['a']['b'] == datetime.datetime(2020, 10, 15, 10, 15, 44, 877000)
|
|
|
|
|
|
def test_complex_json(test_client: Client, table_context: Callable):
|
|
type_available(test_client, 'json')
|
|
if not test_client.min_version('24.10'):
|
|
pytest.skip('Complex JSON broken before 24.10')
|
|
with table_context('new_json_complex', [
|
|
'key Int32',
|
|
'value Tuple(t JSON)'
|
|
]):
|
|
data = [[100, ({'a': 'qwe123', 'b': 'main', 'c': None},)]]
|
|
test_client.insert('new_json_complex', data)
|
|
result = test_client.query('SELECT * FROM new_json_complex ORDER BY key')
|
|
json1 = result.result_set[0][1]
|
|
assert json1['t']['a'] == 'qwe123'
|
|
|
|
|
|
def test_json_str_time(test_client: Client):
|
|
if not test_client.min_version('25.1'):
|
|
pytest.skip('JSON string/numbers bug before 25.1, skipping')
|
|
result = test_client.query("SELECT '{\"timerange\": \"2025-01-01T00:00:00+0000\"}'::JSON").result_set
|
|
assert result[0][0]['timerange'] == datetime.datetime(2025, 1, 1)
|
|
|
|
# The following query is broken -- looks like something to do with Nullable(String) in the Tuple
|
|
# result = test_client.query("SELECT'{\"k\": [123, \"xyz\"]}'::JSON",
|
|
# settings={'input_format_json_read_numbers_as_strings': 0}).result_set
|