clickhouse-driver 0.2.9__cp310-cp310-musllinux_1_1_i686.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.
Files changed (89) hide show
  1. clickhouse_driver/__init__.py +9 -0
  2. clickhouse_driver/block.py +227 -0
  3. clickhouse_driver/blockstreamprofileinfo.py +22 -0
  4. clickhouse_driver/bufferedreader.cpython-310-i386-linux-gnu.so +0 -0
  5. clickhouse_driver/bufferedwriter.cpython-310-i386-linux-gnu.so +0 -0
  6. clickhouse_driver/client.py +812 -0
  7. clickhouse_driver/clientinfo.py +119 -0
  8. clickhouse_driver/columns/__init__.py +0 -0
  9. clickhouse_driver/columns/arraycolumn.py +161 -0
  10. clickhouse_driver/columns/base.py +221 -0
  11. clickhouse_driver/columns/boolcolumn.py +7 -0
  12. clickhouse_driver/columns/datecolumn.py +108 -0
  13. clickhouse_driver/columns/datetimecolumn.py +203 -0
  14. clickhouse_driver/columns/decimalcolumn.py +116 -0
  15. clickhouse_driver/columns/enumcolumn.py +129 -0
  16. clickhouse_driver/columns/exceptions.py +12 -0
  17. clickhouse_driver/columns/floatcolumn.py +34 -0
  18. clickhouse_driver/columns/intcolumn.py +157 -0
  19. clickhouse_driver/columns/intervalcolumn.py +33 -0
  20. clickhouse_driver/columns/ipcolumn.py +118 -0
  21. clickhouse_driver/columns/jsoncolumn.py +37 -0
  22. clickhouse_driver/columns/largeint.cpython-310-i386-linux-gnu.so +0 -0
  23. clickhouse_driver/columns/lowcardinalitycolumn.py +142 -0
  24. clickhouse_driver/columns/mapcolumn.py +73 -0
  25. clickhouse_driver/columns/nestedcolumn.py +10 -0
  26. clickhouse_driver/columns/nothingcolumn.py +13 -0
  27. clickhouse_driver/columns/nullablecolumn.py +7 -0
  28. clickhouse_driver/columns/nullcolumn.py +15 -0
  29. clickhouse_driver/columns/numpy/__init__.py +0 -0
  30. clickhouse_driver/columns/numpy/base.py +47 -0
  31. clickhouse_driver/columns/numpy/boolcolumn.py +8 -0
  32. clickhouse_driver/columns/numpy/datecolumn.py +19 -0
  33. clickhouse_driver/columns/numpy/datetimecolumn.py +146 -0
  34. clickhouse_driver/columns/numpy/floatcolumn.py +24 -0
  35. clickhouse_driver/columns/numpy/intcolumn.py +43 -0
  36. clickhouse_driver/columns/numpy/lowcardinalitycolumn.py +96 -0
  37. clickhouse_driver/columns/numpy/service.py +58 -0
  38. clickhouse_driver/columns/numpy/stringcolumn.py +78 -0
  39. clickhouse_driver/columns/numpy/tuplecolumn.py +37 -0
  40. clickhouse_driver/columns/service.py +185 -0
  41. clickhouse_driver/columns/simpleaggregatefunctioncolumn.py +7 -0
  42. clickhouse_driver/columns/stringcolumn.py +73 -0
  43. clickhouse_driver/columns/tuplecolumn.py +63 -0
  44. clickhouse_driver/columns/util.py +61 -0
  45. clickhouse_driver/columns/uuidcolumn.py +64 -0
  46. clickhouse_driver/compression/__init__.py +28 -0
  47. clickhouse_driver/compression/base.py +87 -0
  48. clickhouse_driver/compression/lz4.py +21 -0
  49. clickhouse_driver/compression/lz4hc.py +9 -0
  50. clickhouse_driver/compression/zstd.py +20 -0
  51. clickhouse_driver/connection.py +793 -0
  52. clickhouse_driver/context.py +36 -0
  53. clickhouse_driver/dbapi/__init__.py +62 -0
  54. clickhouse_driver/dbapi/connection.py +99 -0
  55. clickhouse_driver/dbapi/cursor.py +370 -0
  56. clickhouse_driver/dbapi/errors.py +40 -0
  57. clickhouse_driver/dbapi/extras.py +73 -0
  58. clickhouse_driver/defines.py +58 -0
  59. clickhouse_driver/errors.py +453 -0
  60. clickhouse_driver/log.py +48 -0
  61. clickhouse_driver/numpy/__init__.py +0 -0
  62. clickhouse_driver/numpy/block.py +8 -0
  63. clickhouse_driver/numpy/helpers.py +28 -0
  64. clickhouse_driver/numpy/result.py +123 -0
  65. clickhouse_driver/opentelemetry.py +43 -0
  66. clickhouse_driver/progress.py +44 -0
  67. clickhouse_driver/protocol.py +130 -0
  68. clickhouse_driver/queryprocessingstage.py +8 -0
  69. clickhouse_driver/reader.py +69 -0
  70. clickhouse_driver/readhelpers.py +26 -0
  71. clickhouse_driver/result.py +144 -0
  72. clickhouse_driver/settings/__init__.py +0 -0
  73. clickhouse_driver/settings/available.py +405 -0
  74. clickhouse_driver/settings/types.py +50 -0
  75. clickhouse_driver/settings/writer.py +34 -0
  76. clickhouse_driver/streams/__init__.py +0 -0
  77. clickhouse_driver/streams/compressed.py +88 -0
  78. clickhouse_driver/streams/native.py +108 -0
  79. clickhouse_driver/util/__init__.py +0 -0
  80. clickhouse_driver/util/compat.py +39 -0
  81. clickhouse_driver/util/escape.py +94 -0
  82. clickhouse_driver/util/helpers.py +171 -0
  83. clickhouse_driver/varint.cpython-310-i386-linux-gnu.so +0 -0
  84. clickhouse_driver/writer.py +67 -0
  85. clickhouse_driver-0.2.9.dist-info/LICENSE +21 -0
  86. clickhouse_driver-0.2.9.dist-info/METADATA +202 -0
  87. clickhouse_driver-0.2.9.dist-info/RECORD +89 -0
  88. clickhouse_driver-0.2.9.dist-info/WHEEL +5 -0
  89. clickhouse_driver-0.2.9.dist-info/top_level.txt +1 -0
@@ -0,0 +1,793 @@
1
+ import logging
2
+ import socket
3
+ import ssl
4
+ from collections import deque
5
+ from contextlib import contextmanager
6
+ from sys import platform
7
+ from time import time
8
+ from urllib.parse import urlparse
9
+
10
+ from . import defines
11
+ from . import errors
12
+ from .block import RowOrientedBlock
13
+ from .blockstreamprofileinfo import BlockStreamProfileInfo
14
+ from .bufferedreader import BufferedSocketReader
15
+ from .bufferedwriter import BufferedSocketWriter
16
+ from .clientinfo import ClientInfo
17
+ from .compression import get_compressor_cls
18
+ from .context import Context
19
+ from .log import log_block
20
+ from .progress import Progress
21
+ from .protocol import Compression, ClientPacketTypes, ServerPacketTypes
22
+ from .queryprocessingstage import QueryProcessingStage
23
+ from .reader import read_binary_str, read_binary_uint64
24
+ from .readhelpers import read_exception
25
+ from .settings.writer import write_settings, SettingsFlags
26
+ from .streams.native import BlockInputStream, BlockOutputStream
27
+ from .util.compat import threading
28
+ from .util.escape import escape_params
29
+ from .varint import write_varint, read_varint
30
+ from .writer import write_binary_str
31
+
32
+ logger = logging.getLogger(__name__)
33
+
34
+
35
+ class Packet(object):
36
+ def __init__(self):
37
+ self.type = None
38
+ self.block = None
39
+ self.exception = None
40
+ self.progress = None
41
+ self.profile_info = None
42
+ self.multistring_message = None
43
+
44
+ super(Packet, self).__init__()
45
+
46
+
47
+ class ServerInfo(object):
48
+ def __init__(self, name, version_major, version_minor, version_patch,
49
+ revision, timezone, display_name, used_revision):
50
+ self.name = name
51
+ self.version_major = version_major
52
+ self.version_minor = version_minor
53
+ self.version_patch = version_patch
54
+ self.revision = revision
55
+ self.timezone = timezone
56
+ self.session_timezone = None
57
+ self.display_name = display_name
58
+ self.used_revision = used_revision
59
+
60
+ super(ServerInfo, self).__init__()
61
+
62
+ def get_timezone(self):
63
+ return self.session_timezone or self.timezone
64
+
65
+ def version_tuple(self):
66
+ return self.version_major, self.version_minor, self.version_patch
67
+
68
+ def __repr__(self):
69
+ version = '%s.%s.%s' % (
70
+ self.version_major, self.version_minor, self.version_patch
71
+ )
72
+ items = [
73
+ ('name', self.name),
74
+ ('version', version),
75
+ ('revision', self.revision),
76
+ ('used revision', self.used_revision),
77
+ ('timezone', self.timezone),
78
+ ('display_name', self.display_name)
79
+ ]
80
+
81
+ params = ', '.join('{}={}'.format(key, value) for key, value in items)
82
+ return '<ServerInfo(%s)>' % (params)
83
+
84
+
85
+ class Connection(object):
86
+ """
87
+ Represents connection between client and ClickHouse server.
88
+
89
+ :param host: host with running ClickHouse server.
90
+ :param port: port ClickHouse server is bound to.
91
+ Defaults to ``9000`` if connection is not secured and
92
+ to ``9440`` if connection is secured.
93
+ :param database: database connect to. Defaults to ``'default'``.
94
+ :param user: database user. Defaults to ``'default'``.
95
+ :param password: user's password. Defaults to ``''`` (no password).
96
+ :param client_name: this name will appear in server logs.
97
+ Defaults to ``'python-driver'``.
98
+ :param connect_timeout: timeout for establishing connection.
99
+ Defaults to ``10`` seconds.
100
+ :param send_receive_timeout: timeout for sending and receiving data.
101
+ Defaults to ``300`` seconds.
102
+ :param sync_request_timeout: timeout for server ping.
103
+ Defaults to ``5`` seconds.
104
+ :param compress_block_size: size of compressed block to send.
105
+ Defaults to ``1048576``.
106
+ :param compression: specifies whether or not use compression.
107
+ Defaults to ``False``. Possible choices:
108
+
109
+ * ``True`` is equivalent to ``'lz4'``.
110
+ * ``'lz4'``.
111
+ * ``'lz4hc'`` high-compression variant of
112
+ ``'lz4'``.
113
+ * ``'zstd'``.
114
+
115
+ :param secure: establish secure connection. Defaults to ``False``.
116
+ :param verify: specifies whether a certificate is required and whether it
117
+ will be validated after connection.
118
+ Defaults to ``True``.
119
+ :param ssl_version: see :func:`ssl.wrap_socket` docs.
120
+ :param ca_certs: see :func:`ssl.wrap_socket` docs.
121
+ :param ciphers: see :func:`ssl.wrap_socket` docs.
122
+ :param keyfile: see :func:`ssl.wrap_socket` docs.
123
+ :param certfile: see :func:`ssl.wrap_socket` docs.
124
+ :param server_hostname: Hostname to use in SSL Wrapper construction.
125
+ Defaults to `None` which will send the passed
126
+ host param during SSL initialization. This param
127
+ may be used when connecting over an SSH tunnel
128
+ to correctly identify the desired server via SNI.
129
+ :param alt_hosts: list of alternative hosts for connection.
130
+ Example: alt_hosts=host1:port1,host2:port2.
131
+ :param settings_is_important: ``False`` means unknown settings will be
132
+ ignored, ``True`` means that the query will
133
+ fail with UNKNOWN_SETTING error.
134
+ Defaults to ``False``.
135
+ :param tcp_keepalive: enables `TCP keepalive <https://tldp.org/HOWTO/
136
+ TCP-Keepalive-HOWTO/overview.html>`_ on established
137
+ connection. If is set to ``True``` system keepalive
138
+ settings are used. You can also specify custom
139
+ keepalive setting with tuple:
140
+ ``(idle_time_sec, interval_sec, probes)``.
141
+ Defaults to ``False``.
142
+ :param client_revision: can be used for client version downgrading.
143
+ Defaults to ``None``.
144
+ """
145
+
146
+ def __init__(
147
+ self, host, port=None,
148
+ database=defines.DEFAULT_DATABASE,
149
+ user=defines.DEFAULT_USER, password=defines.DEFAULT_PASSWORD,
150
+ client_name=defines.CLIENT_NAME,
151
+ connect_timeout=defines.DBMS_DEFAULT_CONNECT_TIMEOUT_SEC,
152
+ send_receive_timeout=defines.DBMS_DEFAULT_TIMEOUT_SEC,
153
+ sync_request_timeout=defines.DBMS_DEFAULT_SYNC_REQUEST_TIMEOUT_SEC,
154
+ compress_block_size=defines.DEFAULT_COMPRESS_BLOCK_SIZE,
155
+ compression=False,
156
+ secure=False,
157
+ # Secure socket parameters.
158
+ verify=True, ssl_version=None, ca_certs=None, ciphers=None,
159
+ keyfile=None, certfile=None,
160
+ server_hostname=None,
161
+ alt_hosts=None,
162
+ settings_is_important=False,
163
+ tcp_keepalive=False,
164
+ client_revision=None
165
+ ):
166
+ if secure:
167
+ default_port = defines.DEFAULT_SECURE_PORT
168
+ else:
169
+ default_port = defines.DEFAULT_PORT
170
+
171
+ self.hosts = deque([(host, port or default_port)])
172
+
173
+ if alt_hosts:
174
+ for host in alt_hosts.split(','):
175
+ url = urlparse('clickhouse://' + host)
176
+ self.hosts.append((url.hostname, url.port or default_port))
177
+
178
+ self.database = database
179
+ self.user = user
180
+ self.password = password
181
+ self.client_name = defines.DBMS_NAME + ' ' + client_name
182
+ self.connect_timeout = connect_timeout
183
+ self.send_receive_timeout = send_receive_timeout
184
+ self.sync_request_timeout = sync_request_timeout
185
+ self.settings_is_important = settings_is_important
186
+ self.tcp_keepalive = tcp_keepalive
187
+ self.client_revision = min(
188
+ client_revision or defines.CLIENT_REVISION, defines.CLIENT_REVISION
189
+ )
190
+
191
+ self.secure_socket = secure
192
+ self.verify_cert = verify
193
+
194
+ ssl_options = {}
195
+ if ssl_version is not None:
196
+ ssl_options['ssl_version'] = ssl_version
197
+ if ca_certs is not None:
198
+ ssl_options['ca_certs'] = ca_certs
199
+ if ciphers is not None:
200
+ ssl_options['ciphers'] = ciphers
201
+ if keyfile is not None:
202
+ ssl_options['keyfile'] = keyfile
203
+ if certfile is not None:
204
+ ssl_options['certfile'] = certfile
205
+
206
+ self.ssl_options = ssl_options
207
+
208
+ self.server_hostname = server_hostname
209
+
210
+ # Use LZ4 compression by default.
211
+ if compression is True:
212
+ compression = 'lz4'
213
+
214
+ if compression is False:
215
+ self.compression = Compression.DISABLED
216
+ self.compressor_cls = None
217
+ self.compress_block_size = None
218
+ else:
219
+ self.compression = Compression.ENABLED
220
+ self.compressor_cls = get_compressor_cls(compression)
221
+ self.compress_block_size = compress_block_size
222
+
223
+ self.socket = None
224
+ self.fin = None
225
+ self.fout = None
226
+
227
+ self.connected = False
228
+
229
+ self.client_trace_context = None
230
+ self.server_info = None
231
+ self.context = Context()
232
+
233
+ # Block writer/reader
234
+ self.block_in = None
235
+ self.block_out = None
236
+ self.block_in_raw = None # log blocks are always not compressed
237
+
238
+ self._lock = threading.Lock()
239
+ self.is_query_executing = False
240
+
241
+ super(Connection, self).__init__()
242
+
243
+ def __repr__(self):
244
+ dsn = '%s://%s:***@%s:%s/%s' % (
245
+ 'clickhouses' if self.secure_socket else 'clickhouse',
246
+ self.user, self.host, self.port, self.database
247
+ ) if self.connected else '(not connected)'
248
+
249
+ return '<Connection(dsn=%s, compression=%s)>' % (dsn, self.compression)
250
+
251
+ def get_description(self):
252
+ return '{}:{}'.format(self.host, self.port)
253
+
254
+ def force_connect(self):
255
+ self.check_query_execution()
256
+
257
+ if not self.connected:
258
+ self.connect()
259
+
260
+ elif not self.ping():
261
+ logger.warning('Connection was closed, reconnecting.')
262
+ self.connect()
263
+
264
+ def _create_socket(self, host, port):
265
+ """
266
+ Acts like socket.create_connection, but wraps socket with SSL
267
+ if connection is secure.
268
+ """
269
+ ssl_options = {}
270
+ if self.secure_socket:
271
+ if self.verify_cert:
272
+ cert_reqs = ssl.CERT_REQUIRED
273
+ else:
274
+ cert_reqs = ssl.CERT_NONE
275
+
276
+ ssl_options = self.ssl_options.copy()
277
+ ssl_options['cert_reqs'] = cert_reqs
278
+
279
+ err = None
280
+ for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
281
+ af, socktype, proto, canonname, sa = res
282
+ sock = None
283
+ try:
284
+ sock = socket.socket(af, socktype, proto)
285
+ sock.settimeout(self.connect_timeout)
286
+
287
+ if self.secure_socket:
288
+ ssl_context = self._create_ssl_context(ssl_options)
289
+ sock = ssl_context.wrap_socket(
290
+ sock, server_hostname=self.server_hostname or host)
291
+
292
+ sock.connect(sa)
293
+ return sock
294
+
295
+ except socket.error as _:
296
+ err = _
297
+ if sock is not None:
298
+ sock.close()
299
+
300
+ if err is not None:
301
+ raise err
302
+ else:
303
+ raise socket.error("getaddrinfo returns an empty list")
304
+
305
+ def _create_ssl_context(self, ssl_options):
306
+ purpose = ssl.Purpose.SERVER_AUTH
307
+
308
+ version = ssl_options.get('ssl_version', ssl.PROTOCOL_TLS_CLIENT)
309
+ context = ssl.SSLContext(version)
310
+ context.check_hostname = self.verify_cert
311
+
312
+ if 'ca_certs' in ssl_options:
313
+ context.load_verify_locations(ssl_options['ca_certs'])
314
+ elif ssl_options.get('cert_reqs') != ssl.CERT_NONE:
315
+ context.load_default_certs(purpose)
316
+ if 'ciphers' in ssl_options:
317
+ context.set_ciphers(ssl_options['ciphers'])
318
+
319
+ if 'cert_reqs' in ssl_options:
320
+ context.verify_mode = ssl_options['cert_reqs']
321
+
322
+ if 'certfile' in ssl_options:
323
+ keyfile = ssl_options.get('keyfile')
324
+ context.load_cert_chain(ssl_options['certfile'], keyfile=keyfile)
325
+
326
+ return context
327
+
328
+ def _init_connection(self, host, port):
329
+ self.socket = self._create_socket(host, port)
330
+ self.connected = True
331
+ self.host, self.port = host, port
332
+ self.socket.settimeout(self.send_receive_timeout)
333
+
334
+ # performance tweak
335
+ self.socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
336
+ if self.tcp_keepalive:
337
+ self._set_keepalive()
338
+
339
+ self.fin = BufferedSocketReader(self.socket, defines.BUFFER_SIZE)
340
+ self.fout = BufferedSocketWriter(self.socket, defines.BUFFER_SIZE)
341
+
342
+ self.send_hello()
343
+ self.receive_hello()
344
+
345
+ revision = self.server_info.used_revision
346
+ if revision >= defines.DBMS_MIN_PROTOCOL_VERSION_WITH_ADDENDUM:
347
+ self.send_addendum()
348
+
349
+ self.block_in = self.get_block_in_stream()
350
+ self.block_in_raw = BlockInputStream(self.fin, self.context)
351
+ self.block_out = self.get_block_out_stream()
352
+
353
+ def _set_keepalive(self):
354
+ self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
355
+
356
+ if not isinstance(self.tcp_keepalive, tuple):
357
+ return
358
+
359
+ idle_time_sec, interval_sec, probes = self.tcp_keepalive
360
+
361
+ if platform == 'linux' or platform == 'win32':
362
+ # This should also work for Windows
363
+ # starting with Windows 10, version 1709.
364
+ self.socket.setsockopt(
365
+ socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, idle_time_sec
366
+ )
367
+ self.socket.setsockopt(
368
+ socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, interval_sec
369
+ )
370
+ self.socket.setsockopt(
371
+ socket.IPPROTO_TCP, socket.TCP_KEEPCNT, probes
372
+ )
373
+
374
+ elif platform == 'darwin':
375
+ TCP_KEEPALIVE = 0x10
376
+ # Only interval is available in mac os.
377
+ self.socket.setsockopt(
378
+ socket.IPPROTO_TCP, TCP_KEEPALIVE, interval_sec
379
+ )
380
+
381
+ def _format_connection_error(self, e, host, port):
382
+ err = (e.strerror + ' ') if e.strerror else ''
383
+ return err + '({}:{})'.format(host, port)
384
+
385
+ def connect(self):
386
+ if self.connected:
387
+ self.disconnect()
388
+
389
+ logger.debug(
390
+ 'Connecting. Database: %s. User: %s', self.database, self.user
391
+ )
392
+
393
+ err = None
394
+ for i in range(len(self.hosts)):
395
+ host, port = self.hosts[0]
396
+ logger.debug('Connecting to %s:%s', host, port)
397
+
398
+ try:
399
+ return self._init_connection(host, port)
400
+
401
+ except socket.timeout as e:
402
+ self.disconnect()
403
+ logger.warning(
404
+ 'Failed to connect to %s:%s', host, port, exc_info=True
405
+ )
406
+ err_str = self._format_connection_error(e, host, port)
407
+ err = errors.SocketTimeoutError(err_str)
408
+
409
+ except socket.error as e:
410
+ self.disconnect()
411
+ logger.warning(
412
+ 'Failed to connect to %s:%s', host, port, exc_info=True
413
+ )
414
+ err_str = self._format_connection_error(e, host, port)
415
+ err = errors.NetworkError(err_str)
416
+
417
+ self.hosts.rotate(-1)
418
+
419
+ if err is not None:
420
+ raise err
421
+
422
+ def reset_state(self):
423
+ self.host = None
424
+ self.port = None
425
+ self.socket = None
426
+ self.fin = None
427
+ self.fout = None
428
+
429
+ self.connected = False
430
+
431
+ self.client_trace_context = None
432
+ self.server_info = None
433
+
434
+ self.block_in = None
435
+ self.block_in_raw = None
436
+ self.block_out = None
437
+
438
+ self.is_query_executing = False
439
+
440
+ def disconnect(self):
441
+ """
442
+ Closes connection between server and client.
443
+ Frees resources: e.g. closes socket.
444
+ """
445
+
446
+ if self.connected:
447
+ # There can be errors on shutdown.
448
+ # We need to close socket and reset state even if it happens.
449
+ try:
450
+ self.socket.shutdown(socket.SHUT_RDWR)
451
+
452
+ except socket.error as e:
453
+ logger.warning('Error on socket shutdown: %s', e)
454
+
455
+ self.socket.close()
456
+
457
+ # Socket can be constructed but not connected.
458
+ elif self.socket:
459
+ self.socket.close()
460
+
461
+ self.reset_state()
462
+
463
+ def send_hello(self):
464
+ write_varint(ClientPacketTypes.HELLO, self.fout)
465
+ write_binary_str(self.client_name, self.fout)
466
+ write_varint(defines.CLIENT_VERSION_MAJOR, self.fout)
467
+ write_varint(defines.CLIENT_VERSION_MINOR, self.fout)
468
+ # NOTE For backward compatibility of the protocol,
469
+ # client cannot send its version_patch.
470
+ write_varint(self.client_revision, self.fout)
471
+ write_binary_str(self.database, self.fout)
472
+ write_binary_str(self.user, self.fout)
473
+ write_binary_str(self.password, self.fout)
474
+
475
+ self.fout.flush()
476
+
477
+ def receive_hello(self):
478
+ packet_type = read_varint(self.fin)
479
+
480
+ if packet_type == ServerPacketTypes.HELLO:
481
+ server_name = read_binary_str(self.fin)
482
+ server_version_major = read_varint(self.fin)
483
+ server_version_minor = read_varint(self.fin)
484
+ server_revision = read_varint(self.fin)
485
+
486
+ used_revision = min(self.client_revision, server_revision)
487
+
488
+ server_timezone = None
489
+ if used_revision >= \
490
+ defines.DBMS_MIN_REVISION_WITH_SERVER_TIMEZONE:
491
+ server_timezone = read_binary_str(self.fin)
492
+
493
+ server_display_name = ''
494
+ if used_revision >= \
495
+ defines.DBMS_MIN_REVISION_WITH_SERVER_DISPLAY_NAME:
496
+ server_display_name = read_binary_str(self.fin)
497
+
498
+ server_version_patch = server_revision
499
+ if used_revision >= \
500
+ defines.DBMS_MIN_REVISION_WITH_VERSION_PATCH:
501
+ server_version_patch = read_varint(self.fin)
502
+
503
+ if used_revision >= defines. \
504
+ DBMS_MIN_PROTOCOL_VERSION_WITH_PASSWORD_COMPLEXITY_RULES:
505
+ rules_size = read_varint(self.fin)
506
+ for _i in range(rules_size):
507
+ read_binary_str(self.fin) # original_pattern
508
+ read_binary_str(self.fin) # exception_message
509
+
510
+ if used_revision >= defines. \
511
+ DBMS_MIN_REVISION_WITH_INTERSERVER_SECRET_V2:
512
+ read_binary_uint64(self.fin) # read_nonce
513
+
514
+ self.server_info = ServerInfo(
515
+ server_name, server_version_major, server_version_minor,
516
+ server_version_patch, server_revision,
517
+ server_timezone, server_display_name, used_revision
518
+ )
519
+ self.context.server_info = self.server_info
520
+
521
+ logger.debug(
522
+ 'Connected to %s server version %s.%s.%s, revision: %s',
523
+ server_name, server_version_major, server_version_minor,
524
+ server_version_patch, server_revision
525
+ )
526
+
527
+ elif packet_type == ServerPacketTypes.EXCEPTION:
528
+ raise self.receive_exception()
529
+
530
+ else:
531
+ message = self.unexpected_packet_message('Hello or Exception',
532
+ packet_type)
533
+ self.disconnect()
534
+ raise errors.UnexpectedPacketFromServerError(message)
535
+
536
+ def send_addendum(self):
537
+ revision = self.server_info.used_revision
538
+
539
+ if revision >= defines.DBMS_MIN_PROTOCOL_VERSION_WITH_QUOTA_KEY:
540
+ write_binary_str(
541
+ self.context.client_settings['quota_key'], self.fout
542
+ )
543
+
544
+ def ping(self):
545
+ timeout = self.sync_request_timeout
546
+
547
+ with self.timeout_setter(timeout):
548
+ try:
549
+ write_varint(ClientPacketTypes.PING, self.fout)
550
+ self.fout.flush()
551
+
552
+ packet_type = read_varint(self.fin)
553
+ while packet_type == ServerPacketTypes.PROGRESS:
554
+ self.receive_progress()
555
+ packet_type = read_varint(self.fin)
556
+
557
+ if packet_type != ServerPacketTypes.PONG:
558
+ msg = self.unexpected_packet_message('Pong', packet_type)
559
+ raise errors.UnexpectedPacketFromServerError(msg)
560
+
561
+ except errors.Error:
562
+ raise
563
+
564
+ except (socket.error, EOFError) as e:
565
+ # It's just a warning now.
566
+ # Current connection will be closed, new will be established.
567
+ logger.warning(
568
+ 'Error on %s ping: %s', self.get_description(), e
569
+ )
570
+ return False
571
+
572
+ return True
573
+
574
+ def receive_packet(self):
575
+ packet = Packet()
576
+
577
+ packet.type = packet_type = read_varint(self.fin)
578
+
579
+ if packet_type == ServerPacketTypes.DATA:
580
+ packet.block = self.receive_data(may_be_use_numpy=True)
581
+
582
+ elif packet_type == ServerPacketTypes.EXCEPTION:
583
+ packet.exception = self.receive_exception()
584
+
585
+ elif packet_type == ServerPacketTypes.PROGRESS:
586
+ packet.progress = self.receive_progress()
587
+
588
+ elif packet_type == ServerPacketTypes.PROFILE_INFO:
589
+ packet.profile_info = self.receive_profile_info()
590
+
591
+ elif packet_type == ServerPacketTypes.TOTALS:
592
+ packet.block = self.receive_data()
593
+
594
+ elif packet_type == ServerPacketTypes.EXTREMES:
595
+ packet.block = self.receive_data()
596
+
597
+ elif packet_type == ServerPacketTypes.LOG:
598
+ packet.block = self.receive_data(may_be_compressed=False)
599
+ log_block(packet.block)
600
+
601
+ elif packet_type == ServerPacketTypes.END_OF_STREAM:
602
+ self.is_query_executing = False
603
+ pass
604
+
605
+ elif packet_type == ServerPacketTypes.TABLE_COLUMNS:
606
+ packet.multistring_message = self.receive_multistring_message(
607
+ packet_type
608
+ )
609
+
610
+ elif packet_type == ServerPacketTypes.PART_UUIDS:
611
+ packet.block = self.receive_data()
612
+
613
+ elif packet_type == ServerPacketTypes.READ_TASK_REQUEST:
614
+ packet.block = self.receive_data()
615
+
616
+ elif packet_type == ServerPacketTypes.PROFILE_EVENTS:
617
+ packet.block = self.receive_data(may_be_compressed=False)
618
+
619
+ elif packet_type == ServerPacketTypes.TIMEZONE_UPDATE:
620
+ timezone = read_binary_str(self.fin)
621
+ if timezone:
622
+ logger.info('Server timezone changed to %s', timezone)
623
+ self.server_info.session_timezone = timezone
624
+
625
+ else:
626
+ message = 'Unknown packet {} from server {}'.format(
627
+ packet_type, self.get_description()
628
+ )
629
+ self.disconnect()
630
+ raise errors.UnknownPacketFromServerError(message)
631
+
632
+ return packet
633
+
634
+ def get_block_in_stream(self):
635
+ if self.compression:
636
+ from .streams.compressed import CompressedBlockInputStream
637
+
638
+ return CompressedBlockInputStream(self.fin, self.context)
639
+ else:
640
+ return BlockInputStream(self.fin, self.context)
641
+
642
+ def get_block_out_stream(self):
643
+ if self.compression:
644
+ from .streams.compressed import CompressedBlockOutputStream
645
+
646
+ return CompressedBlockOutputStream(
647
+ self.compressor_cls, self.compress_block_size,
648
+ self.fout, self.context
649
+ )
650
+ else:
651
+ return BlockOutputStream(self.fout, self.context)
652
+
653
+ def receive_data(self, may_be_compressed=True, may_be_use_numpy=False):
654
+ revision = self.server_info.used_revision
655
+
656
+ if revision >= defines.DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES:
657
+ read_binary_str(self.fin)
658
+
659
+ reader = self.block_in if may_be_compressed else self.block_in_raw
660
+ use_numpy = False if not may_be_use_numpy else None
661
+ return reader.read(use_numpy=use_numpy)
662
+
663
+ def receive_exception(self):
664
+ return read_exception(self.fin)
665
+
666
+ def receive_progress(self):
667
+ progress = Progress()
668
+ progress.read(self.server_info, self.fin)
669
+ return progress
670
+
671
+ def receive_profile_info(self):
672
+ profile_info = BlockStreamProfileInfo()
673
+ profile_info.read(self.fin)
674
+ return profile_info
675
+
676
+ def receive_multistring_message(self, packet_type):
677
+ num = ServerPacketTypes.strings_in_message(packet_type)
678
+ return [read_binary_str(self.fin) for _i in range(num)]
679
+
680
+ def send_data(self, block, table_name=''):
681
+ start = time()
682
+ write_varint(ClientPacketTypes.DATA, self.fout)
683
+
684
+ revision = self.server_info.used_revision
685
+ if revision >= defines.DBMS_MIN_REVISION_WITH_TEMPORARY_TABLES:
686
+ write_binary_str(table_name, self.fout)
687
+
688
+ self.block_out.write(block)
689
+ logger.debug('Block "%s" send time: %f', table_name, time() - start)
690
+
691
+ def send_query(self, query, query_id=None, params=None):
692
+ if not self.connected:
693
+ self.connect()
694
+
695
+ write_varint(ClientPacketTypes.QUERY, self.fout)
696
+
697
+ write_binary_str(query_id or '', self.fout)
698
+
699
+ revision = self.server_info.used_revision
700
+ if revision >= defines.DBMS_MIN_REVISION_WITH_CLIENT_INFO:
701
+ client_info = ClientInfo(self.client_name, self.context,
702
+ client_revision=self.client_revision)
703
+ client_info.query_kind = ClientInfo.QueryKind.INITIAL_QUERY
704
+
705
+ client_info.write(revision, self.fout)
706
+
707
+ settings_as_strings = (
708
+ revision >= defines
709
+ .DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS
710
+ )
711
+ settings_flags = 0
712
+ if self.settings_is_important:
713
+ settings_flags |= SettingsFlags.IMPORTANT
714
+ write_settings(self.context.settings, self.fout, settings_as_strings,
715
+ settings_flags)
716
+
717
+ if revision >= defines.DBMS_MIN_REVISION_WITH_INTERSERVER_SECRET:
718
+ write_binary_str('', self.fout)
719
+
720
+ write_varint(QueryProcessingStage.COMPLETE, self.fout)
721
+ write_varint(self.compression, self.fout)
722
+
723
+ write_binary_str(query, self.fout)
724
+
725
+ if revision >= defines.DBMS_MIN_PROTOCOL_VERSION_WITH_PARAMETERS:
726
+ if self.context.client_settings['server_side_params']:
727
+ # Always settings_as_strings = True
728
+ escaped = escape_params(
729
+ params or {}, self.context, for_server=True
730
+ )
731
+ else:
732
+ escaped = {}
733
+ write_settings(escaped, self.fout, True, SettingsFlags.CUSTOM)
734
+
735
+ logger.debug('Query: %s', query)
736
+
737
+ self.fout.flush()
738
+
739
+ def send_cancel(self):
740
+ write_varint(ClientPacketTypes.CANCEL, self.fout)
741
+
742
+ self.fout.flush()
743
+
744
+ def send_external_tables(self, tables, types_check=False):
745
+ for table in tables or []:
746
+ if not table['structure']:
747
+ raise ValueError(
748
+ 'Empty table "{}" structure'.format(table['name'])
749
+ )
750
+
751
+ data = table['data']
752
+ block_cls = RowOrientedBlock
753
+
754
+ if self.context.client_settings['use_numpy']:
755
+ from .numpy.block import NumpyColumnOrientedBlock
756
+
757
+ columns = [x[0] for x in table['structure']]
758
+ data = [data[column].values for column in columns]
759
+
760
+ block_cls = NumpyColumnOrientedBlock
761
+
762
+ block = block_cls(table['structure'], data,
763
+ types_check=types_check)
764
+ self.send_data(block, table_name=table['name'])
765
+
766
+ # Empty block, end of data transfer.
767
+ self.send_data(RowOrientedBlock())
768
+
769
+ @contextmanager
770
+ def timeout_setter(self, new_timeout):
771
+ old_timeout = self.socket.gettimeout()
772
+ self.socket.settimeout(new_timeout)
773
+
774
+ yield
775
+
776
+ self.socket.settimeout(old_timeout)
777
+
778
+ def unexpected_packet_message(self, expected, packet_type):
779
+ packet_type = ServerPacketTypes.to_str(packet_type)
780
+
781
+ return (
782
+ 'Unexpected packet from server {} (expected {}, got {})'
783
+ .format(self.get_description(), expected, packet_type)
784
+ )
785
+
786
+ def check_query_execution(self):
787
+ self._lock.acquire(blocking=False)
788
+
789
+ if self.is_query_executing:
790
+ raise errors.PartiallyConsumedQueryError()
791
+
792
+ self.is_query_executing = True
793
+ self._lock.release()