clickhouse-driver 0.2.5__cp38-cp38-win32.whl → 0.2.7__cp38-cp38-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of clickhouse-driver might be problematic. Click here for more details.
- clickhouse_driver/__init__.py +1 -1
- clickhouse_driver/block.py +3 -2
- clickhouse_driver/bufferedreader.cp38-win32.pyd +0 -0
- clickhouse_driver/bufferedwriter.cp38-win32.pyd +0 -0
- clickhouse_driver/client.py +120 -16
- clickhouse_driver/clientinfo.py +2 -2
- clickhouse_driver/columns/arraycolumn.py +15 -6
- clickhouse_driver/columns/base.py +71 -7
- clickhouse_driver/columns/datecolumn.py +2 -2
- clickhouse_driver/columns/jsoncolumn.py +37 -0
- clickhouse_driver/columns/largeint.cp38-win32.pyd +0 -0
- clickhouse_driver/columns/lowcardinalitycolumn.py +23 -4
- clickhouse_driver/columns/mapcolumn.py +6 -2
- clickhouse_driver/columns/nestedcolumn.py +2 -13
- clickhouse_driver/columns/numpy/datetimecolumn.py +16 -16
- clickhouse_driver/columns/numpy/lowcardinalitycolumn.py +2 -2
- clickhouse_driver/columns/service.py +12 -2
- clickhouse_driver/columns/tuplecolumn.py +31 -5
- clickhouse_driver/columns/uuidcolumn.py +1 -1
- clickhouse_driver/connection.py +104 -15
- clickhouse_driver/defines.py +9 -1
- clickhouse_driver/log.py +7 -3
- clickhouse_driver/progress.py +8 -2
- clickhouse_driver/settings/writer.py +7 -2
- clickhouse_driver/streams/native.py +18 -6
- clickhouse_driver/util/compat.py +12 -0
- clickhouse_driver/util/escape.py +35 -7
- clickhouse_driver/varint.cp38-win32.pyd +0 -0
- {clickhouse_driver-0.2.5.dist-info → clickhouse_driver-0.2.7.dist-info}/METADATA +201 -202
- {clickhouse_driver-0.2.5.dist-info → clickhouse_driver-0.2.7.dist-info}/RECORD +33 -32
- {clickhouse_driver-0.2.5.dist-info → clickhouse_driver-0.2.7.dist-info}/WHEEL +1 -1
- {clickhouse_driver-0.2.5.dist-info → clickhouse_driver-0.2.7.dist-info}/LICENSE +0 -0
- {clickhouse_driver-0.2.5.dist-info → clickhouse_driver-0.2.7.dist-info}/top_level.txt +0 -0
|
@@ -21,12 +21,12 @@ class NumpyDateTimeColumnBase(NumpyColumn):
|
|
|
21
21
|
def apply_timezones_after_read(self, dt):
|
|
22
22
|
timezone = self.timezone if self.timezone else self.local_timezone
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if self.offset_naive:
|
|
24
|
+
if self.offset_naive and timezone.zone != 'UTC':
|
|
25
|
+
ts = pd.to_datetime(dt, utc=True).tz_convert(timezone)
|
|
27
26
|
ts = ts.tz_localize(None)
|
|
27
|
+
return ts.to_numpy(self.datetime_dtype)
|
|
28
28
|
|
|
29
|
-
return
|
|
29
|
+
return dt
|
|
30
30
|
|
|
31
31
|
def apply_timezones_before_write(self, items):
|
|
32
32
|
if isinstance(items, pd.DatetimeIndex):
|
|
@@ -65,7 +65,7 @@ class NumpyDateTimeColumn(NumpyDateTimeColumnBase):
|
|
|
65
65
|
|
|
66
66
|
|
|
67
67
|
class NumpyDateTime64Column(NumpyDateTimeColumnBase):
|
|
68
|
-
dtype = np.dtype(np.
|
|
68
|
+
dtype = np.dtype(np.int64)
|
|
69
69
|
datetime_dtype = 'datetime64[ns]'
|
|
70
70
|
|
|
71
71
|
max_scale = 9
|
|
@@ -75,15 +75,15 @@ class NumpyDateTime64Column(NumpyDateTimeColumnBase):
|
|
|
75
75
|
super(NumpyDateTime64Column, self).__init__(**kwargs)
|
|
76
76
|
|
|
77
77
|
def read_items(self, n_items, buf):
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
78
|
+
# Clickhouse: t seconds is represented as t * 10^scale.
|
|
79
|
+
# datetime64[ns]: t seconds is represented as t * 10^9.
|
|
80
|
+
# Since 0 <= scale <= 9, multiply by the integer 10^(9 - scale).
|
|
81
81
|
items = super(NumpyDateTime64Column, self).read_items(n_items, buf)
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
83
|
+
tmp = np.copy(items)
|
|
84
|
+
tmp *= 10 ** (9 - self.scale)
|
|
85
|
+
dt = tmp.view(dtype='datetime64[ns]')
|
|
85
86
|
|
|
86
|
-
dt = seconds + microseconds
|
|
87
87
|
return self.apply_timezones_after_read(dt)
|
|
88
88
|
|
|
89
89
|
def write_items(self, items, buf):
|
|
@@ -120,12 +120,12 @@ def create_numpy_datetime_column(spec, column_options):
|
|
|
120
120
|
|
|
121
121
|
context = column_options['context']
|
|
122
122
|
|
|
123
|
-
tz_name =
|
|
123
|
+
tz_name = None
|
|
124
124
|
offset_naive = True
|
|
125
125
|
|
|
126
126
|
# As Numpy do not use local timezone for converting timestamp to
|
|
127
127
|
# datetime we need always detect local timezone for manual converting.
|
|
128
|
-
|
|
128
|
+
local_tz_name = get_localzone_name_compat()
|
|
129
129
|
|
|
130
130
|
# Use column's timezone if it's specified.
|
|
131
131
|
if spec and spec[-1] == ')':
|
|
@@ -133,11 +133,11 @@ def create_numpy_datetime_column(spec, column_options):
|
|
|
133
133
|
offset_naive = False
|
|
134
134
|
else:
|
|
135
135
|
if not context.settings.get('use_client_time_zone', False):
|
|
136
|
-
if
|
|
136
|
+
if local_tz_name != context.server_info.timezone:
|
|
137
137
|
tz_name = context.server_info.timezone
|
|
138
138
|
|
|
139
|
-
if tz_name
|
|
140
|
-
|
|
139
|
+
timezone = get_timezone(tz_name) if tz_name else None
|
|
140
|
+
local_timezone = get_timezone(local_tz_name) if local_tz_name else None
|
|
141
141
|
|
|
142
142
|
return cls(timezone=timezone, offset_naive=offset_naive,
|
|
143
143
|
local_timezone=local_timezone, **column_options)
|
|
@@ -37,7 +37,7 @@ class NumpyLowCardinalityColumn(LowCardinalityColumn):
|
|
|
37
37
|
c = pd.Categorical(items)
|
|
38
38
|
|
|
39
39
|
int_type = int(log(len(c.codes), 2) / 8)
|
|
40
|
-
int_column = self.int_types[int_type]()
|
|
40
|
+
int_column = self.int_types[int_type](**self.init_kwargs)
|
|
41
41
|
|
|
42
42
|
serialization_type = self.serialization_type | int_type
|
|
43
43
|
|
|
@@ -66,7 +66,7 @@ class NumpyLowCardinalityColumn(LowCardinalityColumn):
|
|
|
66
66
|
|
|
67
67
|
# Lowest byte contains info about key type.
|
|
68
68
|
key_type = serialization_type & 0xf
|
|
69
|
-
keys_column = self.int_types[key_type]()
|
|
69
|
+
keys_column = self.int_types[key_type](**self.init_kwargs)
|
|
70
70
|
|
|
71
71
|
nullable = self.nested_column.nullable
|
|
72
72
|
# Prevent null map reading. Reset nested column nullable flag.
|
|
@@ -15,6 +15,7 @@ from .intcolumn import (
|
|
|
15
15
|
UInt8Column, UInt16Column, UInt32Column, UInt64Column
|
|
16
16
|
)
|
|
17
17
|
from .lowcardinalitycolumn import create_low_cardinality_column
|
|
18
|
+
from .jsoncolumn import create_json_column
|
|
18
19
|
from .mapcolumn import create_map_column
|
|
19
20
|
from .nothingcolumn import NothingColumn
|
|
20
21
|
from .nullcolumn import NullColumn
|
|
@@ -122,6 +123,11 @@ def get_column_by_spec(spec, column_options, use_numpy=None):
|
|
|
122
123
|
spec, create_column_with_options, column_options
|
|
123
124
|
)
|
|
124
125
|
|
|
126
|
+
elif spec.startswith("Object('json')"):
|
|
127
|
+
return create_json_column(
|
|
128
|
+
spec, create_column_with_options, column_options
|
|
129
|
+
)
|
|
130
|
+
|
|
125
131
|
else:
|
|
126
132
|
for alias, primitive in aliases:
|
|
127
133
|
if spec.startswith(alias):
|
|
@@ -137,8 +143,12 @@ def get_column_by_spec(spec, column_options, use_numpy=None):
|
|
|
137
143
|
raise errors.UnknownTypeError('Unknown type {}'.format(spec))
|
|
138
144
|
|
|
139
145
|
|
|
140
|
-
def read_column(context, column_spec, n_items, buf, use_numpy=None
|
|
141
|
-
|
|
146
|
+
def read_column(context, column_spec, n_items, buf, use_numpy=None,
|
|
147
|
+
has_custom_serialization=False):
|
|
148
|
+
column_options = {
|
|
149
|
+
'context': context,
|
|
150
|
+
'has_custom_serialization': has_custom_serialization
|
|
151
|
+
}
|
|
142
152
|
col = get_column_by_spec(column_spec, column_options, use_numpy=use_numpy)
|
|
143
153
|
col.read_state_prefix(buf)
|
|
144
154
|
return col.read_data(n_items, buf)
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
|
|
2
2
|
from .base import Column
|
|
3
|
-
from .util import get_inner_spec,
|
|
3
|
+
from .util import get_inner_spec, get_inner_columns_with_types
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class TupleColumn(Column):
|
|
7
7
|
py_types = (list, tuple)
|
|
8
8
|
|
|
9
|
-
def __init__(self, nested_columns, **kwargs):
|
|
9
|
+
def __init__(self, names, nested_columns, **kwargs):
|
|
10
|
+
self.names = names
|
|
10
11
|
self.nested_columns = nested_columns
|
|
12
|
+
client_settings = kwargs['context'].client_settings
|
|
13
|
+
settings = kwargs['context'].settings
|
|
14
|
+
self.namedtuple_as_json = (
|
|
15
|
+
settings.get('allow_experimental_object_type', False) and
|
|
16
|
+
client_settings.get('namedtuple_as_json', True)
|
|
17
|
+
)
|
|
18
|
+
|
|
11
19
|
super(TupleColumn, self).__init__(**kwargs)
|
|
12
20
|
self.null_value = tuple(x.null_value for x in nested_columns)
|
|
13
21
|
|
|
@@ -23,15 +31,33 @@ class TupleColumn(Column):
|
|
|
23
31
|
|
|
24
32
|
def read_data(self, n_items, buf):
|
|
25
33
|
rv = [x.read_data(n_items, buf) for x in self.nested_columns]
|
|
26
|
-
|
|
34
|
+
rv = list(zip(*rv))
|
|
35
|
+
|
|
36
|
+
if self.names[0] and self.namedtuple_as_json:
|
|
37
|
+
return [dict(zip(self.names, x)) for x in rv]
|
|
38
|
+
else:
|
|
39
|
+
return rv
|
|
27
40
|
|
|
28
41
|
def read_items(self, n_items, buf):
|
|
29
42
|
return self.read_data(n_items, buf)
|
|
30
43
|
|
|
44
|
+
def read_state_prefix(self, buf):
|
|
45
|
+
super(TupleColumn, self).read_state_prefix(buf)
|
|
46
|
+
|
|
47
|
+
for x in self.nested_columns:
|
|
48
|
+
x.read_state_prefix(buf)
|
|
49
|
+
|
|
50
|
+
def write_state_prefix(self, buf):
|
|
51
|
+
super(TupleColumn, self).write_state_prefix(buf)
|
|
52
|
+
|
|
53
|
+
for x in self.nested_columns:
|
|
54
|
+
x.write_state_prefix(buf)
|
|
55
|
+
|
|
31
56
|
|
|
32
57
|
def create_tuple_column(spec, column_by_spec_getter, column_options):
|
|
33
58
|
inner_spec = get_inner_spec('Tuple', spec)
|
|
34
|
-
|
|
59
|
+
columns_with_types = get_inner_columns_with_types(inner_spec)
|
|
60
|
+
names, types = zip(*columns_with_types)
|
|
35
61
|
|
|
36
|
-
return TupleColumn([column_by_spec_getter(x) for x in
|
|
62
|
+
return TupleColumn(names, [column_by_spec_getter(x) for x in types],
|
|
37
63
|
**column_options)
|
clickhouse_driver/connection.py
CHANGED
|
@@ -3,6 +3,7 @@ import socket
|
|
|
3
3
|
import ssl
|
|
4
4
|
from collections import deque
|
|
5
5
|
from contextlib import contextmanager
|
|
6
|
+
from sys import platform
|
|
6
7
|
from time import time
|
|
7
8
|
from urllib.parse import urlparse
|
|
8
9
|
|
|
@@ -19,11 +20,12 @@ from .log import log_block
|
|
|
19
20
|
from .progress import Progress
|
|
20
21
|
from .protocol import Compression, ClientPacketTypes, ServerPacketTypes
|
|
21
22
|
from .queryprocessingstage import QueryProcessingStage
|
|
22
|
-
from .reader import read_binary_str
|
|
23
|
+
from .reader import read_binary_str, read_binary_uint64
|
|
23
24
|
from .readhelpers import read_exception
|
|
24
|
-
from .settings.writer import write_settings
|
|
25
|
+
from .settings.writer import write_settings, SettingsFlags
|
|
25
26
|
from .streams.native import BlockInputStream, BlockOutputStream
|
|
26
27
|
from .util.compat import threading
|
|
28
|
+
from .util.escape import escape_params
|
|
27
29
|
from .varint import write_varint, read_varint
|
|
28
30
|
from .writer import write_binary_str
|
|
29
31
|
|
|
@@ -44,7 +46,7 @@ class Packet(object):
|
|
|
44
46
|
|
|
45
47
|
class ServerInfo(object):
|
|
46
48
|
def __init__(self, name, version_major, version_minor, version_patch,
|
|
47
|
-
revision, timezone, display_name):
|
|
49
|
+
revision, timezone, display_name, used_revision):
|
|
48
50
|
self.name = name
|
|
49
51
|
self.version_major = version_major
|
|
50
52
|
self.version_minor = version_minor
|
|
@@ -52,6 +54,7 @@ class ServerInfo(object):
|
|
|
52
54
|
self.revision = revision
|
|
53
55
|
self.timezone = timezone
|
|
54
56
|
self.display_name = display_name
|
|
57
|
+
self.used_revision = used_revision
|
|
55
58
|
|
|
56
59
|
super(ServerInfo, self).__init__()
|
|
57
60
|
|
|
@@ -66,6 +69,7 @@ class ServerInfo(object):
|
|
|
66
69
|
('name', self.name),
|
|
67
70
|
('version', version),
|
|
68
71
|
('revision', self.revision),
|
|
72
|
+
('used revision', self.used_revision),
|
|
69
73
|
('timezone', self.timezone),
|
|
70
74
|
('display_name', self.display_name)
|
|
71
75
|
]
|
|
@@ -124,6 +128,15 @@ class Connection(object):
|
|
|
124
128
|
ignored, ``True`` means that the query will
|
|
125
129
|
fail with UNKNOWN_SETTING error.
|
|
126
130
|
Defaults to ``False``.
|
|
131
|
+
:param tcp_keepalive: enables `TCP keepalive <https://tldp.org/HOWTO/
|
|
132
|
+
TCP-Keepalive-HOWTO/overview.html>`_ on established
|
|
133
|
+
connection. If is set to ``True``` system keepalive
|
|
134
|
+
settings are used. You can also specify custom
|
|
135
|
+
keepalive setting with tuple:
|
|
136
|
+
``(idle_time_sec, interval_sec, probes)``.
|
|
137
|
+
Defaults to ``False``.
|
|
138
|
+
:param client_revision: can be used for client version downgrading.
|
|
139
|
+
Defaults to ``None``.
|
|
127
140
|
"""
|
|
128
141
|
|
|
129
142
|
def __init__(
|
|
@@ -143,6 +156,8 @@ class Connection(object):
|
|
|
143
156
|
server_hostname=None,
|
|
144
157
|
alt_hosts=None,
|
|
145
158
|
settings_is_important=False,
|
|
159
|
+
tcp_keepalive=False,
|
|
160
|
+
client_revision=None
|
|
146
161
|
):
|
|
147
162
|
if secure:
|
|
148
163
|
default_port = defines.DEFAULT_SECURE_PORT
|
|
@@ -164,6 +179,10 @@ class Connection(object):
|
|
|
164
179
|
self.send_receive_timeout = send_receive_timeout
|
|
165
180
|
self.sync_request_timeout = sync_request_timeout
|
|
166
181
|
self.settings_is_important = settings_is_important
|
|
182
|
+
self.tcp_keepalive = tcp_keepalive
|
|
183
|
+
self.client_revision = min(
|
|
184
|
+
client_revision or defines.CLIENT_REVISION, defines.CLIENT_REVISION
|
|
185
|
+
)
|
|
167
186
|
|
|
168
187
|
self.secure_socket = secure
|
|
169
188
|
self.verify_cert = verify
|
|
@@ -284,6 +303,7 @@ class Connection(object):
|
|
|
284
303
|
|
|
285
304
|
version = ssl_options.get('ssl_version', ssl.PROTOCOL_TLS)
|
|
286
305
|
context = ssl.SSLContext(version)
|
|
306
|
+
context.check_hostname = self.verify_cert
|
|
287
307
|
|
|
288
308
|
if 'ca_certs' in ssl_options:
|
|
289
309
|
context.load_verify_locations(ssl_options['ca_certs'])
|
|
@@ -310,6 +330,8 @@ class Connection(object):
|
|
|
310
330
|
|
|
311
331
|
# performance tweak
|
|
312
332
|
self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
|
333
|
+
if self.tcp_keepalive:
|
|
334
|
+
self._set_keepalive()
|
|
313
335
|
|
|
314
336
|
self.fin = BufferedSocketReader(self.socket, defines.BUFFER_SIZE)
|
|
315
337
|
self.fout = BufferedSocketWriter(self.socket, defines.BUFFER_SIZE)
|
|
@@ -317,10 +339,42 @@ class Connection(object):
|
|
|
317
339
|
self.send_hello()
|
|
318
340
|
self.receive_hello()
|
|
319
341
|
|
|
342
|
+
revision = self.server_info.used_revision
|
|
343
|
+
if revision >= defines.DBMS_MIN_PROTOCOL_VERSION_WITH_ADDENDUM:
|
|
344
|
+
self.send_addendum()
|
|
345
|
+
|
|
320
346
|
self.block_in = self.get_block_in_stream()
|
|
321
347
|
self.block_in_raw = BlockInputStream(self.fin, self.context)
|
|
322
348
|
self.block_out = self.get_block_out_stream()
|
|
323
349
|
|
|
350
|
+
def _set_keepalive(self):
|
|
351
|
+
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
|
|
352
|
+
|
|
353
|
+
if not isinstance(self.tcp_keepalive, tuple):
|
|
354
|
+
return
|
|
355
|
+
|
|
356
|
+
idle_time_sec, interval_sec, probes = self.tcp_keepalive
|
|
357
|
+
|
|
358
|
+
if platform == 'linux' or platform == 'win32':
|
|
359
|
+
# This should also work for Windows
|
|
360
|
+
# starting with Windows 10, version 1709.
|
|
361
|
+
self.socket.setsockopt(
|
|
362
|
+
socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, idle_time_sec
|
|
363
|
+
)
|
|
364
|
+
self.socket.setsockopt(
|
|
365
|
+
socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, interval_sec
|
|
366
|
+
)
|
|
367
|
+
self.socket.setsockopt(
|
|
368
|
+
socket.IPPROTO_TCP, socket.TCP_KEEPCNT, probes
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
elif platform == 'darwin':
|
|
372
|
+
TCP_KEEPALIVE = 0x10
|
|
373
|
+
# Only interval is available in mac os.
|
|
374
|
+
self.socket.setsockopt(
|
|
375
|
+
socket.IPPROTO_TCP, TCP_KEEPALIVE, interval_sec
|
|
376
|
+
)
|
|
377
|
+
|
|
324
378
|
def _format_connection_error(self, e, host, port):
|
|
325
379
|
err = (e.strerror + ' ') if e.strerror else ''
|
|
326
380
|
return err + '({}:{})'.format(host, port)
|
|
@@ -410,7 +464,7 @@ class Connection(object):
|
|
|
410
464
|
write_varint(defines.CLIENT_VERSION_MINOR, self.fout)
|
|
411
465
|
# NOTE For backward compatibility of the protocol,
|
|
412
466
|
# client cannot send its version_patch.
|
|
413
|
-
write_varint(
|
|
467
|
+
write_varint(self.client_revision, self.fout)
|
|
414
468
|
write_binary_str(self.database, self.fout)
|
|
415
469
|
write_binary_str(self.user, self.fout)
|
|
416
470
|
write_binary_str(self.password, self.fout)
|
|
@@ -426,25 +480,38 @@ class Connection(object):
|
|
|
426
480
|
server_version_minor = read_varint(self.fin)
|
|
427
481
|
server_revision = read_varint(self.fin)
|
|
428
482
|
|
|
483
|
+
used_revision = min(self.client_revision, server_revision)
|
|
484
|
+
|
|
429
485
|
server_timezone = None
|
|
430
|
-
if
|
|
486
|
+
if used_revision >= \
|
|
431
487
|
defines.DBMS_MIN_REVISION_WITH_SERVER_TIMEZONE:
|
|
432
488
|
server_timezone = read_binary_str(self.fin)
|
|
433
489
|
|
|
434
490
|
server_display_name = ''
|
|
435
|
-
if
|
|
491
|
+
if used_revision >= \
|
|
436
492
|
defines.DBMS_MIN_REVISION_WITH_SERVER_DISPLAY_NAME:
|
|
437
493
|
server_display_name = read_binary_str(self.fin)
|
|
438
494
|
|
|
439
495
|
server_version_patch = server_revision
|
|
440
|
-
if
|
|
496
|
+
if used_revision >= \
|
|
441
497
|
defines.DBMS_MIN_REVISION_WITH_VERSION_PATCH:
|
|
442
498
|
server_version_patch = read_varint(self.fin)
|
|
443
499
|
|
|
500
|
+
if used_revision >= defines. \
|
|
501
|
+
DBMS_MIN_PROTOCOL_VERSION_WITH_PASSWORD_COMPLEXITY_RULES:
|
|
502
|
+
rules_size = read_varint(self.fin)
|
|
503
|
+
for _i in range(rules_size):
|
|
504
|
+
read_binary_str(self.fin) # original_pattern
|
|
505
|
+
read_binary_str(self.fin) # exception_message
|
|
506
|
+
|
|
507
|
+
if used_revision >= defines. \
|
|
508
|
+
DBMS_MIN_REVISION_WITH_INTERSERVER_SECRET_V2:
|
|
509
|
+
read_binary_uint64(self.fin) # read_nonce
|
|
510
|
+
|
|
444
511
|
self.server_info = ServerInfo(
|
|
445
512
|
server_name, server_version_major, server_version_minor,
|
|
446
513
|
server_version_patch, server_revision,
|
|
447
|
-
server_timezone, server_display_name
|
|
514
|
+
server_timezone, server_display_name, used_revision
|
|
448
515
|
)
|
|
449
516
|
self.context.server_info = self.server_info
|
|
450
517
|
|
|
@@ -463,6 +530,14 @@ class Connection(object):
|
|
|
463
530
|
self.disconnect()
|
|
464
531
|
raise errors.UnexpectedPacketFromServerError(message)
|
|
465
532
|
|
|
533
|
+
def send_addendum(self):
|
|
534
|
+
revision = self.server_info.used_revision
|
|
535
|
+
|
|
536
|
+
if revision >= defines.DBMS_MIN_PROTOCOL_VERSION_WITH_QUOTA_KEY:
|
|
537
|
+
write_binary_str(
|
|
538
|
+
self.context.client_settings['quota_key'], self.fout
|
|
539
|
+
)
|
|
540
|
+
|
|
466
541
|
def ping(self):
|
|
467
542
|
timeout = self.sync_request_timeout
|
|
468
543
|
|
|
@@ -567,7 +642,7 @@ class Connection(object):
|
|
|
567
642
|
return BlockOutputStream(self.fout, self.context)
|
|
568
643
|
|
|
569
644
|
def receive_data(self, may_be_compressed=True, may_be_use_numpy=False):
|
|
570
|
-
revision = self.server_info.
|
|
645
|
+
revision = self.server_info.used_revision
|
|
571
646
|
|
|
572
647
|
if revision >= defines.DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES:
|
|
573
648
|
read_binary_str(self.fin)
|
|
@@ -581,7 +656,7 @@ class Connection(object):
|
|
|
581
656
|
|
|
582
657
|
def receive_progress(self):
|
|
583
658
|
progress = Progress()
|
|
584
|
-
progress.read(self.server_info
|
|
659
|
+
progress.read(self.server_info, self.fin)
|
|
585
660
|
return progress
|
|
586
661
|
|
|
587
662
|
def receive_profile_info(self):
|
|
@@ -597,14 +672,14 @@ class Connection(object):
|
|
|
597
672
|
start = time()
|
|
598
673
|
write_varint(ClientPacketTypes.DATA, self.fout)
|
|
599
674
|
|
|
600
|
-
revision = self.server_info.
|
|
675
|
+
revision = self.server_info.used_revision
|
|
601
676
|
if revision >= defines.DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES:
|
|
602
677
|
write_binary_str(table_name, self.fout)
|
|
603
678
|
|
|
604
679
|
self.block_out.write(block)
|
|
605
680
|
logger.debug('Block "%s" send time: %f', table_name, time() - start)
|
|
606
681
|
|
|
607
|
-
def send_query(self, query, query_id=None):
|
|
682
|
+
def send_query(self, query, query_id=None, params=None):
|
|
608
683
|
if not self.connected:
|
|
609
684
|
self.connect()
|
|
610
685
|
|
|
@@ -612,9 +687,10 @@ class Connection(object):
|
|
|
612
687
|
|
|
613
688
|
write_binary_str(query_id or '', self.fout)
|
|
614
689
|
|
|
615
|
-
revision = self.server_info.
|
|
690
|
+
revision = self.server_info.used_revision
|
|
616
691
|
if revision >= defines.DBMS_MIN_REVISION_WITH_CLIENT_INFO:
|
|
617
|
-
client_info = ClientInfo(self.client_name, self.context
|
|
692
|
+
client_info = ClientInfo(self.client_name, self.context,
|
|
693
|
+
client_revision=self.client_revision)
|
|
618
694
|
client_info.query_kind = ClientInfo.QueryKind.INITIAL_QUERY
|
|
619
695
|
|
|
620
696
|
client_info.write(revision, self.fout)
|
|
@@ -623,8 +699,11 @@ class Connection(object):
|
|
|
623
699
|
revision >= defines
|
|
624
700
|
.DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS
|
|
625
701
|
)
|
|
702
|
+
settings_flags = 0
|
|
703
|
+
if self.settings_is_important:
|
|
704
|
+
settings_flags |= SettingsFlags.IMPORTANT
|
|
626
705
|
write_settings(self.context.settings, self.fout, settings_as_strings,
|
|
627
|
-
|
|
706
|
+
settings_flags)
|
|
628
707
|
|
|
629
708
|
if revision >= defines.DBMS_MIN_REVISION_WITH_INTERSERVER_SECRET:
|
|
630
709
|
write_binary_str('', self.fout)
|
|
@@ -634,6 +713,16 @@ class Connection(object):
|
|
|
634
713
|
|
|
635
714
|
write_binary_str(query, self.fout)
|
|
636
715
|
|
|
716
|
+
if revision >= defines.DBMS_MIN_PROTOCOL_VERSION_WITH_PARAMETERS:
|
|
717
|
+
if self.context.client_settings['server_side_params']:
|
|
718
|
+
# Always settings_as_strings = True
|
|
719
|
+
escaped = escape_params(
|
|
720
|
+
params or {}, self.context, for_server=True
|
|
721
|
+
)
|
|
722
|
+
else:
|
|
723
|
+
escaped = {}
|
|
724
|
+
write_settings(escaped, self.fout, True, SettingsFlags.CUSTOM)
|
|
725
|
+
|
|
637
726
|
logger.debug('Query: %s', query)
|
|
638
727
|
|
|
639
728
|
self.fout.flush()
|
clickhouse_driver/defines.py
CHANGED
|
@@ -25,6 +25,14 @@ DBMS_MIN_PROTOCOL_VERSION_WITH_DISTRIBUTED_DEPTH = 54448
|
|
|
25
25
|
DBMS_MIN_PROTOCOL_VERSION_WITH_INITIAL_QUERY_START_TIME = 54449
|
|
26
26
|
DBMS_MIN_PROTOCOL_VERSION_WITH_INCREMENTAL_PROFILE_EVENTS = 54451
|
|
27
27
|
DBMS_MIN_REVISION_WITH_PARALLEL_REPLICAS = 54453
|
|
28
|
+
DBMS_MIN_REVISION_WITH_CUSTOM_SERIALIZATION = 54454
|
|
29
|
+
DBMS_MIN_PROTOCOL_VERSION_WITH_PROFILE_EVENTS_IN_INSERT = 54456
|
|
30
|
+
DBMS_MIN_PROTOCOL_VERSION_WITH_ADDENDUM = 54458
|
|
31
|
+
DBMS_MIN_PROTOCOL_VERSION_WITH_QUOTA_KEY = 54458
|
|
32
|
+
DBMS_MIN_PROTOCOL_VERSION_WITH_PARAMETERS = 54459
|
|
33
|
+
DBMS_MIN_PROTOCOL_VERSION_WITH_SERVER_QUERY_TIME_IN_PROGRESS = 54460
|
|
34
|
+
DBMS_MIN_PROTOCOL_VERSION_WITH_PASSWORD_COMPLEXITY_RULES = 54461
|
|
35
|
+
DBMS_MIN_REVISION_WITH_INTERSERVER_SECRET_V2 = 54462
|
|
28
36
|
|
|
29
37
|
# Timeouts
|
|
30
38
|
DBMS_DEFAULT_CONNECT_TIMEOUT_SEC = 10
|
|
@@ -40,7 +48,7 @@ CLIENT_NAME = 'python-driver'
|
|
|
40
48
|
CLIENT_VERSION_MAJOR = 20
|
|
41
49
|
CLIENT_VERSION_MINOR = 10
|
|
42
50
|
CLIENT_VERSION_PATCH = 2
|
|
43
|
-
CLIENT_REVISION =
|
|
51
|
+
CLIENT_REVISION = DBMS_MIN_REVISION_WITH_INTERSERVER_SECRET_V2
|
|
44
52
|
|
|
45
53
|
BUFFER_SIZE = 1048576
|
|
46
54
|
|
clickhouse_driver/log.py
CHANGED
|
@@ -2,7 +2,8 @@ import logging
|
|
|
2
2
|
|
|
3
3
|
logger = logging.getLogger(__name__)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
# Keep in sync with ClickHouse priorities
|
|
6
|
+
# https://github.com/ClickHouse/ClickHouse/blob/master/src/Interpreters/InternalTextLogsQueue.cpp
|
|
6
7
|
log_priorities = (
|
|
7
8
|
'Unknown',
|
|
8
9
|
'Fatal',
|
|
@@ -12,9 +13,12 @@ log_priorities = (
|
|
|
12
13
|
'Notice',
|
|
13
14
|
'Information',
|
|
14
15
|
'Debug',
|
|
15
|
-
'Trace'
|
|
16
|
+
'Trace',
|
|
17
|
+
'Test',
|
|
16
18
|
)
|
|
17
19
|
|
|
20
|
+
num_priorities = len(log_priorities)
|
|
21
|
+
|
|
18
22
|
|
|
19
23
|
def log_block(block):
|
|
20
24
|
if block is None:
|
|
@@ -25,7 +29,7 @@ def log_block(block):
|
|
|
25
29
|
for row in block.get_rows():
|
|
26
30
|
row = dict(zip(column_names, row))
|
|
27
31
|
|
|
28
|
-
if 1 <= row['priority'] <=
|
|
32
|
+
if 1 <= row['priority'] <= num_priorities:
|
|
29
33
|
priority = log_priorities[row['priority']]
|
|
30
34
|
else:
|
|
31
35
|
priority = row[0]
|
clickhouse_driver/progress.py
CHANGED
|
@@ -9,14 +9,15 @@ class Progress(object):
|
|
|
9
9
|
self.total_rows = 0
|
|
10
10
|
self.written_rows = 0
|
|
11
11
|
self.written_bytes = 0
|
|
12
|
+
self.elapsed_ns = 0
|
|
12
13
|
|
|
13
14
|
super(Progress, self).__init__()
|
|
14
15
|
|
|
15
|
-
def read(self,
|
|
16
|
+
def read(self, server_info, fin):
|
|
16
17
|
self.rows = read_varint(fin)
|
|
17
18
|
self.bytes = read_varint(fin)
|
|
18
19
|
|
|
19
|
-
revision =
|
|
20
|
+
revision = server_info.used_revision
|
|
20
21
|
if revision >= defines.DBMS_MIN_REVISION_WITH_TOTAL_ROWS_IN_PROGRESS:
|
|
21
22
|
self.total_rows = read_varint(fin)
|
|
22
23
|
|
|
@@ -24,9 +25,14 @@ class Progress(object):
|
|
|
24
25
|
self.written_rows = read_varint(fin)
|
|
25
26
|
self.written_bytes = read_varint(fin)
|
|
26
27
|
|
|
28
|
+
if revision >= defines. \
|
|
29
|
+
DBMS_MIN_PROTOCOL_VERSION_WITH_SERVER_QUERY_TIME_IN_PROGRESS:
|
|
30
|
+
self.elapsed_ns = read_varint(fin)
|
|
31
|
+
|
|
27
32
|
def increment(self, another_progress):
|
|
28
33
|
self.rows += another_progress.rows
|
|
29
34
|
self.bytes += another_progress.bytes
|
|
30
35
|
self.total_rows += another_progress.total_rows
|
|
31
36
|
self.written_rows += another_progress.written_rows
|
|
32
37
|
self.written_bytes += another_progress.written_bytes
|
|
38
|
+
self.elapsed_ns += another_progress.elapsed_ns
|
|
@@ -7,13 +7,18 @@ from .available import settings as available_settings
|
|
|
7
7
|
logger = logging.getLogger(__name__)
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
class SettingsFlags:
|
|
11
|
+
IMPORTANT = 0x1
|
|
12
|
+
CUSTOM = 0x2
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def write_settings(settings, buf, settings_as_strings, flags):
|
|
11
16
|
for setting, value in (settings or {}).items():
|
|
12
17
|
# If the server support settings as string we do not need to know
|
|
13
18
|
# anything about them, so we can write any setting.
|
|
14
19
|
if settings_as_strings:
|
|
15
20
|
write_binary_str(setting, buf)
|
|
16
|
-
write_binary_uint8(
|
|
21
|
+
write_binary_uint8(flags, buf)
|
|
17
22
|
write_binary_str(str(value), buf)
|
|
18
23
|
|
|
19
24
|
else:
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from ..block import ColumnOrientedBlock, BlockInfo
|
|
2
2
|
from ..columns.service import read_column, write_column
|
|
3
|
-
from ..reader import read_binary_str
|
|
3
|
+
from ..reader import read_binary_str, read_binary_uint8
|
|
4
4
|
from ..varint import write_varint, read_varint
|
|
5
|
-
from ..writer import write_binary_str
|
|
5
|
+
from ..writer import write_binary_str, write_binary_uint8
|
|
6
6
|
from .. import defines
|
|
7
7
|
|
|
8
8
|
|
|
@@ -14,7 +14,7 @@ class BlockOutputStream(object):
|
|
|
14
14
|
super(BlockOutputStream, self).__init__()
|
|
15
15
|
|
|
16
16
|
def write(self, block):
|
|
17
|
-
revision = self.context.server_info.
|
|
17
|
+
revision = self.context.server_info.used_revision
|
|
18
18
|
if revision >= defines.DBMS_MIN_REVISION_WITH_BLOCK_INFO:
|
|
19
19
|
block.info.write(self.fout)
|
|
20
20
|
|
|
@@ -35,6 +35,11 @@ class BlockOutputStream(object):
|
|
|
35
35
|
except IndexError:
|
|
36
36
|
raise ValueError('Different rows length')
|
|
37
37
|
|
|
38
|
+
if revision >= \
|
|
39
|
+
defines.DBMS_MIN_REVISION_WITH_CUSTOM_SERIALIZATION:
|
|
40
|
+
# We write always sparse data without custom serialization.
|
|
41
|
+
write_binary_uint8(0, self.fout)
|
|
42
|
+
|
|
38
43
|
write_column(self.context, col_name, col_type, items,
|
|
39
44
|
self.fout, types_check=block.types_check)
|
|
40
45
|
|
|
@@ -54,7 +59,7 @@ class BlockInputStream(object):
|
|
|
54
59
|
def read(self, use_numpy=None):
|
|
55
60
|
info = BlockInfo()
|
|
56
61
|
|
|
57
|
-
revision = self.context.server_info.
|
|
62
|
+
revision = self.context.server_info.used_revision
|
|
58
63
|
if revision >= defines.DBMS_MIN_REVISION_WITH_BLOCK_INFO:
|
|
59
64
|
info.read(self.fin)
|
|
60
65
|
|
|
@@ -70,9 +75,16 @@ class BlockInputStream(object):
|
|
|
70
75
|
names.append(column_name)
|
|
71
76
|
types.append(column_type)
|
|
72
77
|
|
|
78
|
+
has_custom_serialization = False
|
|
79
|
+
if revision >= defines.DBMS_MIN_REVISION_WITH_CUSTOM_SERIALIZATION:
|
|
80
|
+
has_custom_serialization = bool(read_binary_uint8(self.fin))
|
|
81
|
+
|
|
73
82
|
if n_rows:
|
|
74
|
-
column = read_column(
|
|
75
|
-
|
|
83
|
+
column = read_column(
|
|
84
|
+
self.context, column_type, n_rows,
|
|
85
|
+
self.fin, use_numpy=use_numpy,
|
|
86
|
+
has_custom_serialization=has_custom_serialization
|
|
87
|
+
)
|
|
76
88
|
data.append(column)
|
|
77
89
|
|
|
78
90
|
if self.context.client_settings['use_numpy']:
|
clickhouse_driver/util/compat.py
CHANGED
|
@@ -5,6 +5,18 @@ try:
|
|
|
5
5
|
except ImportError:
|
|
6
6
|
import dummy_threading as threading # noqa: F401
|
|
7
7
|
|
|
8
|
+
import json # noqa: F401
|
|
9
|
+
try:
|
|
10
|
+
import orjson as json # noqa: F811
|
|
11
|
+
except ImportError:
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
import ujson as json # noqa: F811,F401
|
|
16
|
+
except ImportError:
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
8
20
|
try:
|
|
9
21
|
# since tzlocal 4.0+
|
|
10
22
|
# this will avoid warning for get_localzone().key
|