clickhouse-driver 0.2.3__cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl → 0.2.9__cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.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.
- clickhouse_driver/__init__.py +1 -1
- clickhouse_driver/block.py +3 -2
- clickhouse_driver/bufferedreader.cpython-310-s390x-linux-gnu.so +0 -0
- clickhouse_driver/bufferedwriter.cpython-310-s390x-linux-gnu.so +0 -0
- clickhouse_driver/client.py +217 -102
- clickhouse_driver/clientinfo.py +22 -2
- clickhouse_driver/columns/arraycolumn.py +19 -8
- clickhouse_driver/columns/base.py +81 -7
- clickhouse_driver/columns/datecolumn.py +52 -13
- clickhouse_driver/columns/datetimecolumn.py +3 -2
- clickhouse_driver/columns/decimalcolumn.py +0 -4
- clickhouse_driver/columns/enumcolumn.py +27 -17
- clickhouse_driver/columns/jsoncolumn.py +37 -0
- clickhouse_driver/columns/largeint.cpython-310-s390x-linux-gnu.so +0 -0
- clickhouse_driver/columns/lowcardinalitycolumn.py +25 -6
- clickhouse_driver/columns/mapcolumn.py +19 -4
- clickhouse_driver/columns/nestedcolumn.py +4 -67
- clickhouse_driver/columns/numpy/boolcolumn.py +8 -0
- clickhouse_driver/columns/numpy/datetimecolumn.py +23 -20
- clickhouse_driver/columns/numpy/lowcardinalitycolumn.py +5 -4
- clickhouse_driver/columns/numpy/service.py +19 -3
- clickhouse_driver/columns/numpy/tuplecolumn.py +37 -0
- clickhouse_driver/columns/service.py +48 -11
- clickhouse_driver/columns/tuplecolumn.py +32 -34
- clickhouse_driver/columns/util.py +61 -0
- clickhouse_driver/columns/uuidcolumn.py +1 -1
- clickhouse_driver/connection.py +151 -25
- clickhouse_driver/dbapi/connection.py +6 -3
- clickhouse_driver/dbapi/extras.py +73 -0
- clickhouse_driver/defines.py +17 -2
- clickhouse_driver/log.py +7 -3
- clickhouse_driver/numpy/helpers.py +5 -2
- clickhouse_driver/progress.py +15 -3
- clickhouse_driver/protocol.py +28 -3
- clickhouse_driver/settings/writer.py +7 -2
- clickhouse_driver/streams/native.py +25 -7
- clickhouse_driver/util/compat.py +12 -0
- clickhouse_driver/util/escape.py +36 -8
- clickhouse_driver/util/helpers.py +114 -0
- clickhouse_driver/varint.cpython-310-s390x-linux-gnu.so +0 -0
- {clickhouse_driver-0.2.3.dist-info → clickhouse_driver-0.2.9.dist-info}/METADATA +10 -13
- {clickhouse_driver-0.2.3.dist-info → clickhouse_driver-0.2.9.dist-info}/RECORD +76 -71
- {clickhouse_driver-0.2.3.dist-info → clickhouse_driver-0.2.9.dist-info}/WHEEL +1 -1
- {clickhouse_driver-0.2.3.dist-info → clickhouse_driver-0.2.9.dist-info}/LICENSE +0 -0
- {clickhouse_driver-0.2.3.dist-info → clickhouse_driver-0.2.9.dist-info}/top_level.txt +0 -0
clickhouse_driver/__init__.py
CHANGED
clickhouse_driver/block.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
from .columns.util import get_inner_spec, get_inner_columns_with_types
|
|
1
2
|
from .reader import read_varint, read_binary_uint8, read_binary_int32
|
|
2
3
|
from .varint import write_varint
|
|
3
4
|
from .writer import write_binary_uint8, write_binary_int32
|
|
4
|
-
from .columns import nestedcolumn
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class BlockInfo(object):
|
|
@@ -172,7 +172,8 @@ class RowOrientedBlock(BaseBlock):
|
|
|
172
172
|
for name, type_ in columns_with_types:
|
|
173
173
|
cwt = None
|
|
174
174
|
if type_.startswith('Nested'):
|
|
175
|
-
|
|
175
|
+
inner_spec = get_inner_spec('Nested', type_)
|
|
176
|
+
cwt = get_inner_columns_with_types(inner_spec)
|
|
176
177
|
columns_with_cwt.append((name, cwt))
|
|
177
178
|
|
|
178
179
|
for i, row in enumerate(data):
|
|
Binary file
|
|
Binary file
|
clickhouse_driver/client.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import re
|
|
2
|
-
import
|
|
2
|
+
from collections import deque
|
|
3
3
|
from contextlib import contextmanager
|
|
4
4
|
from time import time
|
|
5
5
|
import types
|
|
6
|
-
from urllib.parse import urlparse
|
|
6
|
+
from urllib.parse import urlparse
|
|
7
7
|
|
|
8
8
|
from . import errors, defines
|
|
9
9
|
from .block import ColumnOrientedBlock, RowOrientedBlock
|
|
@@ -14,7 +14,7 @@ from .result import (
|
|
|
14
14
|
IterQueryResult, ProgressQueryResult, QueryResult, QueryInfo
|
|
15
15
|
)
|
|
16
16
|
from .util.escape import escape_params
|
|
17
|
-
from .util.helpers import column_chunks, chunks,
|
|
17
|
+
from .util.helpers import column_chunks, chunks, parse_url
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class Client(object):
|
|
@@ -26,7 +26,7 @@ class Client(object):
|
|
|
26
26
|
for the client settings, see below). Defaults to ``None``
|
|
27
27
|
(no additional settings). See all available settings in
|
|
28
28
|
`ClickHouse docs
|
|
29
|
-
<https://clickhouse.
|
|
29
|
+
<https://clickhouse.com/docs/en/operations/settings/settings/>`_.
|
|
30
30
|
:param \\**kwargs: All other args are passed to the
|
|
31
31
|
:py:class:`~clickhouse_driver.connection.Connection`
|
|
32
32
|
constructor.
|
|
@@ -38,7 +38,7 @@ class Client(object):
|
|
|
38
38
|
Defaults to ``1048576``.
|
|
39
39
|
* ``strings_as_bytes`` -- turns off string column encoding/decoding.
|
|
40
40
|
* ``strings_encoding`` -- specifies string encoding. UTF-8 by default.
|
|
41
|
-
* ``use_numpy`` -- Use
|
|
41
|
+
* ``use_numpy`` -- Use NumPy for columns reading. New in version
|
|
42
42
|
*0.2.0*.
|
|
43
43
|
* ``opentelemetry_traceparent`` -- OpenTelemetry traceparent header as
|
|
44
44
|
described by W3C Trace Context recommendation.
|
|
@@ -49,6 +49,24 @@ class Client(object):
|
|
|
49
49
|
* ``quota_key`` -- A string to differentiate quotas when the user have
|
|
50
50
|
keyed quotas configured on server.
|
|
51
51
|
New in version *0.2.3*.
|
|
52
|
+
* ``input_format_null_as_default`` -- Initialize null fields with
|
|
53
|
+
default values if data type of this field is not
|
|
54
|
+
nullable. Does not work for NumPy. Default: False.
|
|
55
|
+
New in version *0.2.4*.
|
|
56
|
+
* ``round_robin`` -- If ``alt_hosts`` are provided the query will be
|
|
57
|
+
executed on host picked with round-robin algorithm.
|
|
58
|
+
New in version *0.2.5*.
|
|
59
|
+
* ``namedtuple_as_json`` -- Controls named tuple and nested types
|
|
60
|
+
deserialization. To interpret these column alongside
|
|
61
|
+
with ``allow_experimental_object_type=1`` as Python
|
|
62
|
+
tuple set ``namedtuple_as_json`` to ``False``.
|
|
63
|
+
Default: True.
|
|
64
|
+
New in version *0.2.6*.
|
|
65
|
+
* ``server_side_params`` -- Species on which side query parameters
|
|
66
|
+
should be rendered into placeholders.
|
|
67
|
+
Default: False. Means that parameters are rendered
|
|
68
|
+
on driver's side.
|
|
69
|
+
New in version *0.2.7*.
|
|
52
70
|
"""
|
|
53
71
|
|
|
54
72
|
available_client_settings = (
|
|
@@ -58,7 +76,10 @@ class Client(object):
|
|
|
58
76
|
'use_numpy',
|
|
59
77
|
'opentelemetry_traceparent',
|
|
60
78
|
'opentelemetry_tracestate',
|
|
61
|
-
'quota_key'
|
|
79
|
+
'quota_key',
|
|
80
|
+
'input_format_null_as_default',
|
|
81
|
+
'namedtuple_as_json',
|
|
82
|
+
'server_side_params'
|
|
62
83
|
)
|
|
63
84
|
|
|
64
85
|
def __init__(self, *args, **kwargs):
|
|
@@ -85,6 +106,15 @@ class Client(object):
|
|
|
85
106
|
),
|
|
86
107
|
'quota_key': self.settings.pop(
|
|
87
108
|
'quota_key', ''
|
|
109
|
+
),
|
|
110
|
+
'input_format_null_as_default': self.settings.pop(
|
|
111
|
+
'input_format_null_as_default', False
|
|
112
|
+
),
|
|
113
|
+
'namedtuple_as_json': self.settings.pop(
|
|
114
|
+
'namedtuple_as_json', True
|
|
115
|
+
),
|
|
116
|
+
'server_side_params': self.settings.pop(
|
|
117
|
+
'server_side_params', False
|
|
88
118
|
)
|
|
89
119
|
}
|
|
90
120
|
|
|
@@ -104,9 +134,33 @@ class Client(object):
|
|
|
104
134
|
self.iter_query_result_cls = IterQueryResult
|
|
105
135
|
self.progress_query_result_cls = ProgressQueryResult
|
|
106
136
|
|
|
107
|
-
|
|
108
|
-
self.
|
|
109
|
-
|
|
137
|
+
round_robin = kwargs.pop('round_robin', False)
|
|
138
|
+
self.connections = deque([Connection(*args, **kwargs)])
|
|
139
|
+
|
|
140
|
+
if round_robin and 'alt_hosts' in kwargs:
|
|
141
|
+
alt_hosts = kwargs.pop('alt_hosts')
|
|
142
|
+
for host in alt_hosts.split(','):
|
|
143
|
+
url = urlparse('clickhouse://' + host)
|
|
144
|
+
|
|
145
|
+
connection_kwargs = kwargs.copy()
|
|
146
|
+
num_args = len(args)
|
|
147
|
+
if num_args >= 2:
|
|
148
|
+
# host and port as positional arguments
|
|
149
|
+
connection_args = (url.hostname, url.port) + args[2:]
|
|
150
|
+
elif num_args >= 1:
|
|
151
|
+
# host as positional and port as keyword argument
|
|
152
|
+
connection_args = (url.hostname, ) + args[1:]
|
|
153
|
+
connection_kwargs['port'] = url.port
|
|
154
|
+
else:
|
|
155
|
+
# host and port as keyword arguments
|
|
156
|
+
connection_args = tuple()
|
|
157
|
+
connection_kwargs['host'] = url.hostname
|
|
158
|
+
connection_kwargs['port'] = url.port
|
|
159
|
+
|
|
160
|
+
connection = Connection(*connection_args, **connection_kwargs)
|
|
161
|
+
self.connections.append(connection)
|
|
162
|
+
|
|
163
|
+
self.connection = self.get_connection()
|
|
110
164
|
self.reset_last_query()
|
|
111
165
|
super(Client, self).__init__()
|
|
112
166
|
|
|
@@ -116,7 +170,22 @@ class Client(object):
|
|
|
116
170
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
117
171
|
self.disconnect()
|
|
118
172
|
|
|
173
|
+
def get_connection(self):
|
|
174
|
+
if hasattr(self, 'connection'):
|
|
175
|
+
self.connections.append(self.connection)
|
|
176
|
+
|
|
177
|
+
connection = self.connections.popleft()
|
|
178
|
+
|
|
179
|
+
connection.context.settings = self.settings
|
|
180
|
+
connection.context.client_settings = self.client_settings
|
|
181
|
+
return connection
|
|
182
|
+
|
|
119
183
|
def disconnect(self):
|
|
184
|
+
self.disconnect_connection()
|
|
185
|
+
for connection in self.connections:
|
|
186
|
+
connection.disconnect()
|
|
187
|
+
|
|
188
|
+
def disconnect_connection(self):
|
|
120
189
|
"""
|
|
121
190
|
Disconnects from the server.
|
|
122
191
|
"""
|
|
@@ -219,13 +288,30 @@ class Client(object):
|
|
|
219
288
|
if query.lower().startswith('use '):
|
|
220
289
|
self.connection.database = query[4:].strip()
|
|
221
290
|
|
|
291
|
+
def establish_connection(self, settings):
|
|
292
|
+
num_connections = len(self.connections)
|
|
293
|
+
if hasattr(self, 'connection'):
|
|
294
|
+
num_connections += 1
|
|
295
|
+
|
|
296
|
+
for i in range(num_connections):
|
|
297
|
+
try:
|
|
298
|
+
self.connection = self.get_connection()
|
|
299
|
+
self.make_query_settings(settings)
|
|
300
|
+
self.connection.force_connect()
|
|
301
|
+
self.last_query = QueryInfo()
|
|
302
|
+
|
|
303
|
+
except (errors.SocketTimeoutError, errors.NetworkError):
|
|
304
|
+
if i < num_connections - 1:
|
|
305
|
+
continue
|
|
306
|
+
raise
|
|
307
|
+
|
|
308
|
+
return
|
|
309
|
+
|
|
222
310
|
@contextmanager
|
|
223
311
|
def disconnect_on_error(self, query, settings):
|
|
224
|
-
self.make_query_settings(settings)
|
|
225
|
-
|
|
226
312
|
try:
|
|
227
|
-
self.
|
|
228
|
-
self.
|
|
313
|
+
self.establish_connection(settings)
|
|
314
|
+
self.connection.server_info.session_timezone = None
|
|
229
315
|
|
|
230
316
|
yield
|
|
231
317
|
|
|
@@ -342,7 +428,7 @@ class Client(object):
|
|
|
342
428
|
def execute_iter(
|
|
343
429
|
self, query, params=None, with_column_types=False,
|
|
344
430
|
external_tables=None, query_id=None, settings=None,
|
|
345
|
-
types_check=False):
|
|
431
|
+
types_check=False, chunk_size=1):
|
|
346
432
|
"""
|
|
347
433
|
*New in version 0.0.14.*
|
|
348
434
|
|
|
@@ -364,19 +450,20 @@ class Client(object):
|
|
|
364
450
|
Defaults to ``None`` (no additional settings).
|
|
365
451
|
:param types_check: enables type checking of data for INSERT queries.
|
|
366
452
|
Causes additional overhead. Defaults to ``False``.
|
|
453
|
+
:param chunk_size: chunk query results.
|
|
367
454
|
:return: :ref:`iter-query-result` proxy.
|
|
368
455
|
"""
|
|
369
|
-
|
|
370
456
|
with self.disconnect_on_error(query, settings):
|
|
371
|
-
|
|
457
|
+
rv = self.iter_process_ordinary_query(
|
|
372
458
|
query, params=params, with_column_types=with_column_types,
|
|
373
459
|
external_tables=external_tables,
|
|
374
460
|
query_id=query_id, types_check=types_check
|
|
375
461
|
)
|
|
462
|
+
return chunks(rv, chunk_size) if chunk_size > 1 else rv
|
|
376
463
|
|
|
377
464
|
def query_dataframe(
|
|
378
465
|
self, query, params=None, external_tables=None, query_id=None,
|
|
379
|
-
settings=None):
|
|
466
|
+
settings=None, replace_nonwords=True):
|
|
380
467
|
"""
|
|
381
468
|
*New in version 0.2.0.*
|
|
382
469
|
|
|
@@ -391,6 +478,8 @@ class Client(object):
|
|
|
391
478
|
ClickHouse server will generate it.
|
|
392
479
|
:param settings: dictionary of query settings.
|
|
393
480
|
Defaults to ``None`` (no additional settings).
|
|
481
|
+
:param replace_nonwords: boolean to replace non-words in column names
|
|
482
|
+
to underscores. Defaults to ``True``.
|
|
394
483
|
:return: pandas DataFrame.
|
|
395
484
|
"""
|
|
396
485
|
|
|
@@ -405,8 +494,12 @@ class Client(object):
|
|
|
405
494
|
settings=settings
|
|
406
495
|
)
|
|
407
496
|
|
|
497
|
+
columns = [name for name, type_ in columns]
|
|
498
|
+
if replace_nonwords:
|
|
499
|
+
columns = [re.sub(r'\W', '_', x) for x in columns]
|
|
500
|
+
|
|
408
501
|
return pd.DataFrame(
|
|
409
|
-
{
|
|
502
|
+
{col: d for d, col in zip(data, columns)}, columns=columns
|
|
410
503
|
)
|
|
411
504
|
|
|
412
505
|
def insert_dataframe(
|
|
@@ -443,6 +536,12 @@ class Client(object):
|
|
|
443
536
|
rv = None
|
|
444
537
|
if sample_block:
|
|
445
538
|
columns = [x[0] for x in sample_block.columns_with_types]
|
|
539
|
+
# raise if any columns are missing from the dataframe
|
|
540
|
+
diff = set(columns) - set(dataframe.columns)
|
|
541
|
+
if len(diff):
|
|
542
|
+
msg = "DataFrame missing required columns: {}"
|
|
543
|
+
raise ValueError(msg.format(list(diff)))
|
|
544
|
+
|
|
446
545
|
data = [dataframe[column].values for column in columns]
|
|
447
546
|
rv = self.send_data(sample_block, data, columnar=True)
|
|
448
547
|
self.receive_end_of_query()
|
|
@@ -460,7 +559,7 @@ class Client(object):
|
|
|
460
559
|
query, params, self.connection.context
|
|
461
560
|
)
|
|
462
561
|
|
|
463
|
-
self.connection.send_query(query, query_id=query_id)
|
|
562
|
+
self.connection.send_query(query, query_id=query_id, params=params)
|
|
464
563
|
self.connection.send_external_tables(external_tables,
|
|
465
564
|
types_check=types_check)
|
|
466
565
|
return self.receive_result(with_column_types=with_column_types,
|
|
@@ -475,8 +574,7 @@ class Client(object):
|
|
|
475
574
|
query = self.substitute_params(
|
|
476
575
|
query, params, self.connection.context
|
|
477
576
|
)
|
|
478
|
-
|
|
479
|
-
self.connection.send_query(query, query_id=query_id)
|
|
577
|
+
self.connection.send_query(query, query_id=query_id, params=params)
|
|
480
578
|
self.connection.send_external_tables(external_tables,
|
|
481
579
|
types_check=types_check)
|
|
482
580
|
return self.receive_result(with_column_types=with_column_types,
|
|
@@ -492,7 +590,7 @@ class Client(object):
|
|
|
492
590
|
query, params, self.connection.context
|
|
493
591
|
)
|
|
494
592
|
|
|
495
|
-
self.connection.send_query(query, query_id=query_id)
|
|
593
|
+
self.connection.send_query(query, query_id=query_id, params=params)
|
|
496
594
|
self.connection.send_external_tables(external_tables,
|
|
497
595
|
types_check=types_check)
|
|
498
596
|
return self.iter_receive_result(with_column_types=with_column_types)
|
|
@@ -503,12 +601,12 @@ class Client(object):
|
|
|
503
601
|
self.connection.send_query(query_without_data, query_id=query_id)
|
|
504
602
|
self.connection.send_external_tables(external_tables,
|
|
505
603
|
types_check=types_check)
|
|
506
|
-
|
|
507
604
|
sample_block = self.receive_sample_block()
|
|
605
|
+
|
|
508
606
|
if sample_block:
|
|
509
607
|
rv = self.send_data(sample_block, data,
|
|
510
608
|
types_check=types_check, columnar=columnar)
|
|
511
|
-
self.
|
|
609
|
+
self.receive_end_of_insert_query()
|
|
512
610
|
return rv
|
|
513
611
|
|
|
514
612
|
def receive_sample_block(self):
|
|
@@ -562,8 +660,15 @@ class Client(object):
|
|
|
562
660
|
self.connection.send_data(block)
|
|
563
661
|
inserted_rows += block.num_rows
|
|
564
662
|
|
|
663
|
+
# Starting from the specific revision there are profile events
|
|
664
|
+
# sent by server in response to each inserted block
|
|
665
|
+
self.receive_profile_events()
|
|
666
|
+
|
|
565
667
|
# Empty block means end of data.
|
|
566
668
|
self.connection.send_data(block_cls())
|
|
669
|
+
# If enabled by revision profile events are also sent after empty block
|
|
670
|
+
self.receive_profile_events()
|
|
671
|
+
|
|
567
672
|
return inserted_rows
|
|
568
673
|
|
|
569
674
|
def receive_end_of_query(self):
|
|
@@ -573,6 +678,9 @@ class Client(object):
|
|
|
573
678
|
if packet.type == ServerPacketTypes.END_OF_STREAM:
|
|
574
679
|
break
|
|
575
680
|
|
|
681
|
+
elif packet.type == ServerPacketTypes.PROGRESS:
|
|
682
|
+
self.last_query.store_progress(packet.progress)
|
|
683
|
+
|
|
576
684
|
elif packet.type == ServerPacketTypes.EXCEPTION:
|
|
577
685
|
raise packet.exception
|
|
578
686
|
|
|
@@ -582,9 +690,69 @@ class Client(object):
|
|
|
582
690
|
elif packet.type == ServerPacketTypes.TABLE_COLUMNS:
|
|
583
691
|
pass
|
|
584
692
|
|
|
693
|
+
elif packet.type == ServerPacketTypes.PROFILE_EVENTS:
|
|
694
|
+
self.last_query.store_profile(packet.profile_info)
|
|
695
|
+
|
|
696
|
+
else:
|
|
697
|
+
message = self.connection.unexpected_packet_message(
|
|
698
|
+
'Exception, EndOfStream, Progress, TableColumns, '
|
|
699
|
+
'ProfileEvents or Log', packet.type
|
|
700
|
+
)
|
|
701
|
+
raise errors.UnexpectedPacketFromServerError(message)
|
|
702
|
+
|
|
703
|
+
def receive_end_of_insert_query(self):
|
|
704
|
+
while True:
|
|
705
|
+
packet = self.connection.receive_packet()
|
|
706
|
+
|
|
707
|
+
if packet.type == ServerPacketTypes.END_OF_STREAM:
|
|
708
|
+
break
|
|
709
|
+
|
|
710
|
+
elif packet.type == ServerPacketTypes.LOG:
|
|
711
|
+
log_block(packet.block)
|
|
712
|
+
|
|
713
|
+
elif packet.type == ServerPacketTypes.PROGRESS:
|
|
714
|
+
self.last_query.store_progress(packet.progress)
|
|
715
|
+
|
|
716
|
+
elif packet.type == ServerPacketTypes.EXCEPTION:
|
|
717
|
+
raise packet.exception
|
|
718
|
+
|
|
719
|
+
else:
|
|
720
|
+
message = self.connection.unexpected_packet_message(
|
|
721
|
+
'EndOfStream, Log, Progress or Exception', packet.type
|
|
722
|
+
)
|
|
723
|
+
raise errors.UnexpectedPacketFromServerError(message)
|
|
724
|
+
|
|
725
|
+
def receive_profile_events(self):
|
|
726
|
+
revision = self.connection.server_info.used_revision
|
|
727
|
+
if (
|
|
728
|
+
revision <
|
|
729
|
+
defines.DBMS_MIN_PROTOCOL_VERSION_WITH_PROFILE_EVENTS_IN_INSERT
|
|
730
|
+
):
|
|
731
|
+
return None
|
|
732
|
+
|
|
733
|
+
while True:
|
|
734
|
+
packet = self.connection.receive_packet()
|
|
735
|
+
|
|
736
|
+
if packet.type == ServerPacketTypes.PROFILE_EVENTS:
|
|
737
|
+
self.last_query.store_profile(packet.profile_info)
|
|
738
|
+
break
|
|
739
|
+
|
|
740
|
+
elif packet.type == ServerPacketTypes.PROGRESS:
|
|
741
|
+
self.last_query.store_progress(packet.progress)
|
|
742
|
+
|
|
743
|
+
elif packet.type == ServerPacketTypes.LOG:
|
|
744
|
+
log_block(packet.block)
|
|
745
|
+
|
|
746
|
+
elif packet.type == ServerPacketTypes.EXCEPTION:
|
|
747
|
+
raise packet.exception
|
|
748
|
+
|
|
749
|
+
elif packet.type == ServerPacketTypes.TIMEZONE_UPDATE:
|
|
750
|
+
pass
|
|
751
|
+
|
|
585
752
|
else:
|
|
586
753
|
message = self.connection.unexpected_packet_message(
|
|
587
|
-
'
|
|
754
|
+
'ProfileEvents, Progress, Log, Exception or '
|
|
755
|
+
'TimezoneUpdate', packet.type
|
|
588
756
|
)
|
|
589
757
|
raise errors.UnexpectedPacketFromServerError(message)
|
|
590
758
|
|
|
@@ -595,6 +763,26 @@ class Client(object):
|
|
|
595
763
|
return self.receive_result(with_column_types=with_column_types)
|
|
596
764
|
|
|
597
765
|
def substitute_params(self, query, params, context):
|
|
766
|
+
"""
|
|
767
|
+
Substitutes parameters into a provided query.
|
|
768
|
+
|
|
769
|
+
For example::
|
|
770
|
+
|
|
771
|
+
client = Client(...)
|
|
772
|
+
|
|
773
|
+
substituted_query = client.substitute_params(
|
|
774
|
+
query='SELECT 1234, %(foo)s',
|
|
775
|
+
params={'foo': 'bar'},
|
|
776
|
+
context=client.connection.context
|
|
777
|
+
)
|
|
778
|
+
|
|
779
|
+
# prints: SELECT 1234, 'bar'
|
|
780
|
+
print(substituted_query)
|
|
781
|
+
"""
|
|
782
|
+
# In case of server side templating we don't substitute here.
|
|
783
|
+
if self.connection.context.client_settings['server_side_params']:
|
|
784
|
+
return query
|
|
785
|
+
|
|
598
786
|
if not isinstance(params, dict):
|
|
599
787
|
raise ValueError('Parameters are expected in dict form')
|
|
600
788
|
|
|
@@ -612,86 +800,13 @@ class Client(object):
|
|
|
612
800
|
clickhouses://[user:password]@localhost:9440/default
|
|
613
801
|
|
|
614
802
|
Three URL schemes are supported:
|
|
615
|
-
|
|
616
|
-
|
|
803
|
+
|
|
804
|
+
* clickhouse:// creates a normal TCP socket connection
|
|
805
|
+
* clickhouses:// creates a SSL wrapped TCP socket connection
|
|
617
806
|
|
|
618
807
|
Any additional querystring arguments will be passed along to
|
|
619
808
|
the Connection class's initializer.
|
|
620
809
|
"""
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
settings = {}
|
|
624
|
-
kwargs = {}
|
|
625
|
-
|
|
626
|
-
host = url.hostname
|
|
627
|
-
|
|
628
|
-
if url.port is not None:
|
|
629
|
-
kwargs['port'] = url.port
|
|
630
|
-
|
|
631
|
-
path = url.path.replace('/', '', 1)
|
|
632
|
-
if path:
|
|
633
|
-
kwargs['database'] = path
|
|
634
|
-
|
|
635
|
-
if url.username is not None:
|
|
636
|
-
kwargs['user'] = unquote(url.username)
|
|
637
|
-
|
|
638
|
-
if url.password is not None:
|
|
639
|
-
kwargs['password'] = unquote(url.password)
|
|
640
|
-
|
|
641
|
-
if url.scheme == 'clickhouses':
|
|
642
|
-
kwargs['secure'] = True
|
|
643
|
-
|
|
644
|
-
compression_algs = {'lz4', 'lz4hc', 'zstd'}
|
|
645
|
-
timeouts = {
|
|
646
|
-
'connect_timeout',
|
|
647
|
-
'send_receive_timeout',
|
|
648
|
-
'sync_request_timeout'
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
for name, value in parse_qs(url.query).items():
|
|
652
|
-
if not value or not len(value):
|
|
653
|
-
continue
|
|
654
|
-
|
|
655
|
-
value = value[0]
|
|
656
|
-
|
|
657
|
-
if name == 'compression':
|
|
658
|
-
value = value.lower()
|
|
659
|
-
if value in compression_algs:
|
|
660
|
-
kwargs[name] = value
|
|
661
|
-
else:
|
|
662
|
-
kwargs[name] = asbool(value)
|
|
663
|
-
|
|
664
|
-
elif name == 'secure':
|
|
665
|
-
kwargs[name] = asbool(value)
|
|
666
|
-
|
|
667
|
-
elif name == 'use_numpy':
|
|
668
|
-
settings[name] = asbool(value)
|
|
669
|
-
|
|
670
|
-
elif name == 'client_name':
|
|
671
|
-
kwargs[name] = value
|
|
672
|
-
|
|
673
|
-
elif name in timeouts:
|
|
674
|
-
kwargs[name] = float(value)
|
|
675
|
-
|
|
676
|
-
elif name == 'compress_block_size':
|
|
677
|
-
kwargs[name] = int(value)
|
|
678
|
-
|
|
679
|
-
elif name == 'settings_is_important':
|
|
680
|
-
kwargs[name] = asbool(value)
|
|
681
|
-
|
|
682
|
-
# ssl
|
|
683
|
-
elif name == 'verify':
|
|
684
|
-
kwargs[name] = asbool(value)
|
|
685
|
-
elif name == 'ssl_version':
|
|
686
|
-
kwargs[name] = getattr(ssl, value)
|
|
687
|
-
elif name in ['ca_certs', 'ciphers', 'keyfile', 'certfile']:
|
|
688
|
-
kwargs[name] = value
|
|
689
|
-
elif name == 'alt_hosts':
|
|
690
|
-
kwargs['alt_hosts'] = value
|
|
691
|
-
else:
|
|
692
|
-
settings[name] = value
|
|
693
|
-
|
|
694
|
-
if settings:
|
|
695
|
-
kwargs['settings'] = settings
|
|
810
|
+
host, kwargs = parse_url(url)
|
|
696
811
|
|
|
697
812
|
return cls(host, **kwargs)
|
clickhouse_driver/clientinfo.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import socket
|
|
2
2
|
import getpass
|
|
3
|
+
from time import time
|
|
3
4
|
|
|
4
5
|
from . import defines
|
|
5
6
|
from . import errors
|
|
@@ -27,14 +28,13 @@ class ClientInfo(object):
|
|
|
27
28
|
client_version_major = defines.CLIENT_VERSION_MAJOR
|
|
28
29
|
client_version_minor = defines.CLIENT_VERSION_MINOR
|
|
29
30
|
client_version_patch = defines.CLIENT_VERSION_PATCH
|
|
30
|
-
client_revision = defines.CLIENT_REVISION
|
|
31
31
|
interface = Interface.TCP
|
|
32
32
|
|
|
33
33
|
initial_user = ''
|
|
34
34
|
initial_query_id = ''
|
|
35
35
|
initial_address = '0.0.0.0:0'
|
|
36
36
|
|
|
37
|
-
def __init__(self, client_name, context):
|
|
37
|
+
def __init__(self, client_name, context, client_revision):
|
|
38
38
|
self.query_kind = ClientInfo.QueryKind.NO_QUERY
|
|
39
39
|
|
|
40
40
|
try:
|
|
@@ -43,6 +43,7 @@ class ClientInfo(object):
|
|
|
43
43
|
self.os_user = ''
|
|
44
44
|
self.client_hostname = socket.gethostname()
|
|
45
45
|
self.client_name = client_name
|
|
46
|
+
self.client_revision = client_revision
|
|
46
47
|
|
|
47
48
|
self.client_trace_context = OpenTelemetryTraceContext(
|
|
48
49
|
context.client_settings['opentelemetry_traceparent'],
|
|
@@ -50,6 +51,8 @@ class ClientInfo(object):
|
|
|
50
51
|
)
|
|
51
52
|
|
|
52
53
|
self.quota_key = context.client_settings['quota_key']
|
|
54
|
+
self.distributed_depth = 0
|
|
55
|
+
self.initial_query_start_time_microseconds = int(time() * 1000000)
|
|
53
56
|
|
|
54
57
|
super(ClientInfo, self).__init__()
|
|
55
58
|
|
|
@@ -71,6 +74,14 @@ class ClientInfo(object):
|
|
|
71
74
|
write_binary_str(self.initial_query_id, fout)
|
|
72
75
|
write_binary_str(self.initial_address, fout)
|
|
73
76
|
|
|
77
|
+
if (
|
|
78
|
+
revision >=
|
|
79
|
+
defines.DBMS_MIN_PROTOCOL_VERSION_WITH_INITIAL_QUERY_START_TIME
|
|
80
|
+
):
|
|
81
|
+
write_binary_uint64(
|
|
82
|
+
self.initial_query_start_time_microseconds, fout
|
|
83
|
+
)
|
|
84
|
+
|
|
74
85
|
write_binary_uint8(self.interface, fout)
|
|
75
86
|
|
|
76
87
|
write_binary_str(self.os_user, fout)
|
|
@@ -83,6 +94,10 @@ class ClientInfo(object):
|
|
|
83
94
|
if revision >= defines.DBMS_MIN_REVISION_WITH_QUOTA_KEY_IN_CLIENT_INFO:
|
|
84
95
|
write_binary_str(self.quota_key, fout)
|
|
85
96
|
|
|
97
|
+
if revision >= \
|
|
98
|
+
defines.DBMS_MIN_PROTOCOL_VERSION_WITH_DISTRIBUTED_DEPTH:
|
|
99
|
+
write_varint(self.distributed_depth, fout)
|
|
100
|
+
|
|
86
101
|
if revision >= defines.DBMS_MIN_REVISION_WITH_VERSION_PATCH:
|
|
87
102
|
write_varint(self.client_version_patch, fout)
|
|
88
103
|
|
|
@@ -97,3 +112,8 @@ class ClientInfo(object):
|
|
|
97
112
|
else:
|
|
98
113
|
# Don't have OpenTelemetry header.
|
|
99
114
|
write_binary_uint8(0, fout)
|
|
115
|
+
|
|
116
|
+
if revision >= defines.DBMS_MIN_REVISION_WITH_PARALLEL_REPLICAS:
|
|
117
|
+
write_varint(0, fout) # collaborate_with_initiator
|
|
118
|
+
write_varint(0, fout) # count_participating_replicas
|
|
119
|
+
write_varint(0, fout) # number_of_current_replica
|
|
@@ -28,25 +28,31 @@ class ArrayColumn(Column):
|
|
|
28
28
|
py_types = (list, tuple)
|
|
29
29
|
|
|
30
30
|
def __init__(self, nested_column, **kwargs):
|
|
31
|
-
self.
|
|
31
|
+
self.init_kwargs = kwargs
|
|
32
|
+
self.size_column = UInt64Column(**kwargs)
|
|
32
33
|
self.nested_column = nested_column
|
|
33
34
|
self._write_depth_0_size = True
|
|
34
35
|
super(ArrayColumn, self).__init__(**kwargs)
|
|
36
|
+
self.null_value = []
|
|
35
37
|
|
|
36
38
|
def write_data(self, data, buf):
|
|
37
39
|
# Column of Array(T) is stored in "compact" format and passed to server
|
|
38
40
|
# wrapped into another Array without size of wrapper array.
|
|
39
|
-
self.nested_column = ArrayColumn(
|
|
41
|
+
self.nested_column = ArrayColumn(
|
|
42
|
+
self.nested_column, **self.init_kwargs
|
|
43
|
+
)
|
|
40
44
|
self.nested_column.nullable = self.nullable
|
|
41
45
|
self.nullable = False
|
|
42
46
|
self._write_depth_0_size = False
|
|
43
47
|
self._write(data, buf)
|
|
44
48
|
|
|
45
|
-
def read_data(self,
|
|
46
|
-
self.nested_column = ArrayColumn(
|
|
49
|
+
def read_data(self, n_rows, buf):
|
|
50
|
+
self.nested_column = ArrayColumn(
|
|
51
|
+
self.nested_column, **self.init_kwargs
|
|
52
|
+
)
|
|
47
53
|
self.nested_column.nullable = self.nullable
|
|
48
54
|
self.nullable = False
|
|
49
|
-
return self._read(
|
|
55
|
+
return self._read(n_rows, buf)[0]
|
|
50
56
|
|
|
51
57
|
def _write_sizes(self, value, buf):
|
|
52
58
|
nulls_map = []
|
|
@@ -99,14 +105,19 @@ class ArrayColumn(Column):
|
|
|
99
105
|
self.nested_column._write_nulls_map(value, buf)
|
|
100
106
|
|
|
101
107
|
def _write(self, value, buf):
|
|
108
|
+
value = self.prepare_items(value)
|
|
102
109
|
self._write_sizes(value, buf)
|
|
103
110
|
self._write_nulls_data(value, buf)
|
|
104
111
|
self._write_data(value, buf)
|
|
105
112
|
|
|
106
113
|
def read_state_prefix(self, buf):
|
|
107
|
-
|
|
114
|
+
super(ArrayColumn, self).read_state_prefix(buf)
|
|
115
|
+
|
|
116
|
+
self.nested_column.read_state_prefix(buf)
|
|
108
117
|
|
|
109
118
|
def write_state_prefix(self, buf):
|
|
119
|
+
super(ArrayColumn, self).write_state_prefix(buf)
|
|
120
|
+
|
|
110
121
|
self.nested_column.write_state_prefix(buf)
|
|
111
122
|
|
|
112
123
|
def _read(self, size, buf):
|
|
@@ -145,6 +156,6 @@ class ArrayColumn(Column):
|
|
|
145
156
|
return tuple(data)
|
|
146
157
|
|
|
147
158
|
|
|
148
|
-
def create_array_column(spec, column_by_spec_getter):
|
|
159
|
+
def create_array_column(spec, column_by_spec_getter, column_options):
|
|
149
160
|
inner = spec[6:-1]
|
|
150
|
-
return ArrayColumn(column_by_spec_getter(inner))
|
|
161
|
+
return ArrayColumn(column_by_spec_getter(inner), **column_options)
|