clickhouse-driver 0.2.5__cp38-cp38-win32.whl → 0.2.7__cp38-cp38-win32.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of clickhouse-driver might be problematic. Click here for more details.

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