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