clickhouse-driver 0.2.8__cp312-cp312-win32.whl → 0.2.10__cp312-cp312-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.
@@ -3,7 +3,7 @@ from .client import Client
3
3
  from .dbapi import connect
4
4
 
5
5
 
6
- VERSION = (0, 2, 8)
6
+ VERSION = (0, 2, 10)
7
7
  __version__ = '.'.join(str(x) for x in VERSION)
8
8
 
9
9
  __all__ = ['Client', 'connect']
@@ -1,10 +1,9 @@
1
1
  import re
2
- import ssl
3
2
  from collections import deque
4
3
  from contextlib import contextmanager
5
4
  from time import time
6
5
  import types
7
- from urllib.parse import urlparse, parse_qs, unquote
6
+ from urllib.parse import urlparse
8
7
 
9
8
  from . import errors, defines
10
9
  from .block import ColumnOrientedBlock, RowOrientedBlock
@@ -15,7 +14,7 @@ from .result import (
15
14
  IterQueryResult, ProgressQueryResult, QueryResult, QueryInfo
16
15
  )
17
16
  from .util.escape import escape_params
18
- from .util.helpers import column_chunks, chunks, asbool
17
+ from .util.helpers import column_chunks, chunks, parse_url
19
18
 
20
19
 
21
20
  class Client(object):
@@ -312,6 +311,7 @@ class Client(object):
312
311
  def disconnect_on_error(self, query, settings):
313
312
  try:
314
313
  self.establish_connection(settings)
314
+ self.connection.server_info.session_timezone = None
315
315
 
316
316
  yield
317
317
 
@@ -746,9 +746,13 @@ class Client(object):
746
746
  elif packet.type == ServerPacketTypes.EXCEPTION:
747
747
  raise packet.exception
748
748
 
749
+ elif packet.type == ServerPacketTypes.TIMEZONE_UPDATE:
750
+ pass
751
+
749
752
  else:
750
753
  message = self.connection.unexpected_packet_message(
751
- 'ProfileEvents, Progress, Log or Exception', packet.type
754
+ 'ProfileEvents, Progress, Log, Exception or '
755
+ 'TimezoneUpdate', packet.type
752
756
  )
753
757
  raise errors.UnexpectedPacketFromServerError(message)
754
758
 
@@ -796,101 +800,13 @@ class Client(object):
796
800
  clickhouses://[user:password]@localhost:9440/default
797
801
 
798
802
  Three URL schemes are supported:
799
- clickhouse:// creates a normal TCP socket connection
800
- clickhouses:// creates a SSL wrapped TCP socket connection
803
+
804
+ * clickhouse:// creates a normal TCP socket connection
805
+ * clickhouses:// creates a SSL wrapped TCP socket connection
801
806
 
802
807
  Any additional querystring arguments will be passed along to
803
808
  the Connection class's initializer.
804
809
  """
805
- url = urlparse(url)
806
-
807
- settings = {}
808
- kwargs = {}
809
-
810
- host = url.hostname
811
-
812
- if url.port is not None:
813
- kwargs['port'] = url.port
814
-
815
- path = url.path.replace('/', '', 1)
816
- if path:
817
- kwargs['database'] = path
818
-
819
- if url.username is not None:
820
- kwargs['user'] = unquote(url.username)
821
-
822
- if url.password is not None:
823
- kwargs['password'] = unquote(url.password)
824
-
825
- if url.scheme == 'clickhouses':
826
- kwargs['secure'] = True
827
-
828
- compression_algs = {'lz4', 'lz4hc', 'zstd'}
829
- timeouts = {
830
- 'connect_timeout',
831
- 'send_receive_timeout',
832
- 'sync_request_timeout'
833
- }
834
-
835
- for name, value in parse_qs(url.query).items():
836
- if not value or not len(value):
837
- continue
838
-
839
- value = value[0]
840
-
841
- if name == 'compression':
842
- value = value.lower()
843
- if value in compression_algs:
844
- kwargs[name] = value
845
- else:
846
- kwargs[name] = asbool(value)
847
-
848
- elif name == 'secure':
849
- kwargs[name] = asbool(value)
850
-
851
- elif name == 'use_numpy':
852
- settings[name] = asbool(value)
853
-
854
- elif name == 'round_robin':
855
- kwargs[name] = asbool(value)
856
-
857
- elif name == 'client_name':
858
- kwargs[name] = value
859
-
860
- elif name in timeouts:
861
- kwargs[name] = float(value)
862
-
863
- elif name == 'compress_block_size':
864
- kwargs[name] = int(value)
865
-
866
- elif name == 'settings_is_important':
867
- kwargs[name] = asbool(value)
868
-
869
- elif name == 'tcp_keepalive':
870
- try:
871
- kwargs[name] = asbool(value)
872
- except ValueError:
873
- parts = value.split(',')
874
- kwargs[name] = (
875
- int(parts[0]), int(parts[1]), int(parts[2])
876
- )
877
- elif name == 'client_revision':
878
- kwargs[name] = int(value)
879
-
880
- # ssl
881
- elif name == 'verify':
882
- kwargs[name] = asbool(value)
883
- elif name == 'ssl_version':
884
- kwargs[name] = getattr(ssl, value)
885
- elif name in ['ca_certs', 'ciphers', 'keyfile', 'certfile',
886
- 'server_hostname']:
887
- kwargs[name] = value
888
- elif name == 'alt_hosts':
889
- kwargs['alt_hosts'] = value
890
- else:
891
- settings[name] = value
892
-
893
- if settings:
894
- kwargs['settings'] = settings
810
+ host, kwargs = parse_url(url)
895
811
 
896
812
  return cls(host, **kwargs)
@@ -39,7 +39,7 @@ class ClientInfo(object):
39
39
 
40
40
  try:
41
41
  self.os_user = getpass.getuser()
42
- except KeyError:
42
+ except (KeyError, OSError):
43
43
  self.os_user = ''
44
44
  self.client_hostname = socket.gethostname()
45
45
  self.client_name = client_name
@@ -193,8 +193,9 @@ def create_datetime_column(spec, column_options):
193
193
  else:
194
194
  if not context.settings.get('use_client_time_zone', False):
195
195
  local_timezone = get_localzone_name_compat()
196
- if local_timezone != context.server_info.timezone:
197
- tz_name = context.server_info.timezone
196
+ remote_timezone = context.server_info.get_timezone()
197
+ if local_timezone != remote_timezone:
198
+ tz_name = remote_timezone
198
199
 
199
200
  if tz_name:
200
201
  timezone = get_timezone(tz_name)
@@ -1,20 +1,24 @@
1
1
  from enum import Enum
2
+ from collections import OrderedDict
2
3
 
3
4
  from .. import errors
4
5
  from .intcolumn import IntColumn
5
6
 
7
+ invalid_names_for_python_enum = frozenset(['mro', ''])
8
+
6
9
 
7
10
  class EnumColumn(IntColumn):
8
11
  py_types = (Enum, int, str)
9
12
 
10
- def __init__(self, enum_cls, **kwargs):
11
- self.enum_cls = enum_cls
13
+ def __init__(self, name_by_value, value_by_name, **kwargs):
14
+ self.name_by_value = name_by_value
15
+ self.value_by_name = value_by_name
12
16
  super(EnumColumn, self).__init__(**kwargs)
13
17
 
14
18
  def before_write_items(self, items, nulls_map=None):
15
19
  null_value = self.null_value
16
-
17
- enum_cls = self.enum_cls
20
+ name_by_value = self.name_by_value
21
+ value_by_name = self.value_by_name
18
22
 
19
23
  for i, item in enumerate(items):
20
24
  if nulls_map and nulls_map[i]:
@@ -26,15 +30,15 @@ class EnumColumn(IntColumn):
26
30
  # Check real enum value
27
31
  try:
28
32
  if isinstance(source_value, str):
29
- items[i] = enum_cls[source_value].value
33
+ items[i] = value_by_name[source_value]
30
34
  else:
31
- items[i] = enum_cls(source_value).value
35
+ items[i] = value_by_name[name_by_value[source_value]]
32
36
  except (ValueError, KeyError):
33
37
  choices = ', '.join(
34
- "'{}' = {}".format(x.name.replace("'", r"\'"), x.value)
35
- for x in enum_cls
38
+ "'{}' = {}".format(name.replace("'", r"\'"), value)
39
+ for name, value in value_by_name.items()
36
40
  )
37
- enum_str = '{}({})'.format(enum_cls.__name__, choices)
41
+ enum_str = '{}({})'.format(self.ch_type, choices)
38
42
 
39
43
  raise errors.LogicalError(
40
44
  "Unknown element '{}' for type {}"
@@ -42,13 +46,13 @@ class EnumColumn(IntColumn):
42
46
  )
43
47
 
44
48
  def after_read_items(self, items, nulls_map=None):
45
- enum_cls = self.enum_cls
49
+ name_by_value = self.name_by_value
46
50
 
47
51
  if nulls_map is None:
48
- return tuple(enum_cls(item).name for item in items)
52
+ return tuple(name_by_value[item] for item in items)
49
53
  else:
50
54
  return tuple(
51
- (None if is_null else enum_cls(items[i]).name)
55
+ (None if is_null else name_by_value[items[i]])
52
56
  for i, is_null in enumerate(nulls_map)
53
57
  )
54
58
 
@@ -73,11 +77,13 @@ def create_enum_column(spec, column_options):
73
77
  params = spec[7:-1]
74
78
  cls = Enum16Column
75
79
 
76
- return cls(Enum(cls.ch_type, _parse_options(params)), **column_options)
80
+ name_by_value, value_by_name = _parse_options(params)
81
+
82
+ return cls(name_by_value, value_by_name, **column_options)
77
83
 
78
84
 
79
85
  def _parse_options(option_string):
80
- options = dict()
86
+ name_by_value, value_by_name = {}, OrderedDict()
81
87
  after_name = False
82
88
  escaped = False
83
89
  quote_character = None
@@ -93,7 +99,9 @@ def _parse_options(option_string):
93
99
  if ch in (' ', '='):
94
100
  pass
95
101
  elif ch == ',':
96
- options[name] = int(value)
102
+ value = int(value)
103
+ name_by_value[value] = name
104
+ value_by_name[name] = value
97
105
  after_name = False
98
106
  name = ''
99
107
  value = '' # reset before collecting new option
@@ -114,6 +122,8 @@ def _parse_options(option_string):
114
122
  quote_character = ch
115
123
 
116
124
  if after_name:
117
- options.setdefault(name, int(value)) # append word after last comma
125
+ value = int(value)
126
+ name_by_value[value] = name
127
+ value_by_name[name] = value
118
128
 
119
- return options
129
+ return name_by_value, value_by_name
@@ -33,7 +33,9 @@ class NumpyDateTimeColumnBase(NumpyColumn):
33
33
  ts = items
34
34
  else:
35
35
  timezone = self.timezone if self.timezone else self.local_timezone
36
- ts = pd.to_datetime(items).tz_localize(timezone)
36
+ ts = pd.to_datetime(items)
37
+ if not getattr(ts.dtype, 'tz', None):
38
+ ts = ts.tz_localize(timezone)
37
39
 
38
40
  ts = ts.tz_convert('UTC')
39
41
  return ts.tz_localize(None).to_numpy(self.datetime_dtype)
@@ -133,8 +135,9 @@ def create_numpy_datetime_column(spec, column_options):
133
135
  offset_naive = False
134
136
  else:
135
137
  if not context.settings.get('use_client_time_zone', False):
136
- if local_tz_name != context.server_info.timezone:
137
- tz_name = context.server_info.timezone
138
+ remote_timezone = context.server_info.get_timezone()
139
+ if local_tz_name != remote_timezone:
140
+ tz_name = remote_timezone
138
141
 
139
142
  timezone = get_timezone(tz_name) if tz_name else None
140
143
  local_timezone = get_timezone(local_tz_name) if local_tz_name else None
@@ -37,12 +37,13 @@ def get_inner_columns(spec):
37
37
 
38
38
 
39
39
  def get_inner_columns_with_types(spec):
40
+ spec = spec.strip()
40
41
  brackets = 0
41
42
  prev_comma = 0
42
43
  prev_space = 0
43
44
 
44
45
  columns = []
45
- for i, x in enumerate(spec + ','):
46
+ for i, x in enumerate(spec.strip() + ','):
46
47
  if x == ',':
47
48
  if brackets == 0:
48
49
  columns.append((
@@ -1,8 +1,11 @@
1
1
  import importlib
2
+ import logging
2
3
 
3
4
  from .. import errors
4
5
  from ..protocol import CompressionMethodByte
5
6
 
7
+ logger = logging.getLogger(__name__)
8
+
6
9
 
7
10
  def get_compressor_cls(alg):
8
11
  try:
@@ -10,6 +13,7 @@ def get_compressor_cls(alg):
10
13
  return module.Compressor
11
14
 
12
15
  except ImportError:
16
+ logger.warning('Unable to import module %s', alg, exc_info=True)
13
17
  raise errors.UnknownCompressionMethod(
14
18
  "Unknown compression method: '{}'".format(alg)
15
19
  )
@@ -7,6 +7,11 @@ from sys import platform
7
7
  from time import time
8
8
  from urllib.parse import urlparse
9
9
 
10
+ try:
11
+ import certifi
12
+ except ImportError:
13
+ certifi = None
14
+
10
15
  from . import defines
11
16
  from . import errors
12
17
  from .block import RowOrientedBlock
@@ -53,11 +58,15 @@ class ServerInfo(object):
53
58
  self.version_patch = version_patch
54
59
  self.revision = revision
55
60
  self.timezone = timezone
61
+ self.session_timezone = None
56
62
  self.display_name = display_name
57
63
  self.used_revision = used_revision
58
64
 
59
65
  super(ServerInfo, self).__init__()
60
66
 
67
+ def get_timezone(self):
68
+ return self.session_timezone or self.timezone
69
+
61
70
  def version_tuple(self):
62
71
  return self.version_major, self.version_minor, self.version_patch
63
72
 
@@ -116,7 +125,10 @@ class Connection(object):
116
125
  :param ca_certs: see :func:`ssl.wrap_socket` docs.
117
126
  :param ciphers: see :func:`ssl.wrap_socket` docs.
118
127
  :param keyfile: see :func:`ssl.wrap_socket` docs.
128
+ :param keypass: see :func:`ssl.wrap_socket` docs.
119
129
  :param certfile: see :func:`ssl.wrap_socket` docs.
130
+ :param check_hostname: see :func:`ssl.wrap_socket` docs.
131
+ Defaults to ``True``.
120
132
  :param server_hostname: Hostname to use in SSL Wrapper construction.
121
133
  Defaults to `None` which will send the passed
122
134
  host param during SSL initialization. This param
@@ -137,6 +149,10 @@ class Connection(object):
137
149
  Defaults to ``False``.
138
150
  :param client_revision: can be used for client version downgrading.
139
151
  Defaults to ``None``.
152
+ :param disable_reconnect: disable automatic reconnect in case of
153
+ failed ``ping``, helpful when every reconnect
154
+ need to be caught in calling code.
155
+ Defaults to ``False``.
140
156
  """
141
157
 
142
158
  def __init__(
@@ -152,12 +168,13 @@ class Connection(object):
152
168
  secure=False,
153
169
  # Secure socket parameters.
154
170
  verify=True, ssl_version=None, ca_certs=None, ciphers=None,
155
- keyfile=None, certfile=None,
171
+ keyfile=None, keypass=None, certfile=None, check_hostname=True,
156
172
  server_hostname=None,
157
173
  alt_hosts=None,
158
174
  settings_is_important=False,
159
175
  tcp_keepalive=False,
160
- client_revision=None
176
+ client_revision=None,
177
+ disable_reconnect=False,
161
178
  ):
162
179
  if secure:
163
180
  default_port = defines.DEFAULT_SECURE_PORT
@@ -183,10 +200,14 @@ class Connection(object):
183
200
  self.client_revision = min(
184
201
  client_revision or defines.CLIENT_REVISION, defines.CLIENT_REVISION
185
202
  )
203
+ self.disable_reconnect = disable_reconnect
186
204
 
187
205
  self.secure_socket = secure
188
206
  self.verify_cert = verify
189
207
 
208
+ if certifi is not None:
209
+ ca_certs = ca_certs or certifi.where()
210
+
190
211
  ssl_options = {}
191
212
  if ssl_version is not None:
192
213
  ssl_options['ssl_version'] = ssl_version
@@ -196,11 +217,14 @@ class Connection(object):
196
217
  ssl_options['ciphers'] = ciphers
197
218
  if keyfile is not None:
198
219
  ssl_options['keyfile'] = keyfile
220
+ if keypass is not None:
221
+ ssl_options['keypass'] = keypass
199
222
  if certfile is not None:
200
223
  ssl_options['certfile'] = certfile
201
224
 
202
225
  self.ssl_options = ssl_options
203
226
 
227
+ self.check_hostname = check_hostname if self.verify_cert else False
204
228
  self.server_hostname = server_hostname
205
229
 
206
230
  # Use LZ4 compression by default.
@@ -254,6 +278,11 @@ class Connection(object):
254
278
  self.connect()
255
279
 
256
280
  elif not self.ping():
281
+ if self.disable_reconnect:
282
+ raise errors.NetworkError(
283
+ "Connection was closed, reconnect is disabled."
284
+ )
285
+
257
286
  logger.warning('Connection was closed, reconnecting.')
258
287
  self.connect()
259
288
 
@@ -301,15 +330,14 @@ class Connection(object):
301
330
  def _create_ssl_context(self, ssl_options):
302
331
  purpose = ssl.Purpose.SERVER_AUTH
303
332
 
304
- version = ssl_options.get('ssl_version', ssl.PROTOCOL_TLS)
333
+ version = ssl_options.get('ssl_version', ssl.PROTOCOL_TLS_CLIENT)
305
334
  context = ssl.SSLContext(version)
306
- context.check_hostname = self.verify_cert
335
+ context.check_hostname = self.check_hostname
307
336
 
308
337
  if 'ca_certs' in ssl_options:
309
338
  context.load_verify_locations(ssl_options['ca_certs'])
310
339
  elif ssl_options.get('cert_reqs') != ssl.CERT_NONE:
311
- context.load_default_certs(purpose
312
- )
340
+ context.load_default_certs(purpose)
313
341
  if 'ciphers' in ssl_options:
314
342
  context.set_ciphers(ssl_options['ciphers'])
315
343
 
@@ -318,7 +346,11 @@ class Connection(object):
318
346
 
319
347
  if 'certfile' in ssl_options:
320
348
  keyfile = ssl_options.get('keyfile')
321
- context.load_cert_chain(ssl_options['certfile'], keyfile=keyfile)
349
+ keypass = ssl_options.get('keypass')
350
+ context.load_cert_chain(
351
+ ssl_options['certfile'],
352
+ keyfile=keyfile, password=keypass
353
+ )
322
354
 
323
355
  return context
324
356
 
@@ -539,6 +571,9 @@ class Connection(object):
539
571
  )
540
572
 
541
573
  def ping(self):
574
+ if not self.socket:
575
+ return None
576
+
542
577
  timeout = self.sync_request_timeout
543
578
 
544
579
  with self.timeout_setter(timeout):
@@ -613,6 +648,12 @@ class Connection(object):
613
648
  elif packet_type == ServerPacketTypes.PROFILE_EVENTS:
614
649
  packet.block = self.receive_data(may_be_compressed=False)
615
650
 
651
+ elif packet_type == ServerPacketTypes.TIMEZONE_UPDATE:
652
+ timezone = read_binary_str(self.fin)
653
+ if timezone:
654
+ logger.info('Server timezone changed to %s', timezone)
655
+ self.server_info.session_timezone = timezone
656
+
616
657
  else:
617
658
  message = 'Unknown packet {} from server {}'.format(
618
659
  packet_type, self.get_description()
@@ -33,6 +33,9 @@ DBMS_MIN_PROTOCOL_VERSION_WITH_PARAMETERS = 54459
33
33
  DBMS_MIN_PROTOCOL_VERSION_WITH_SERVER_QUERY_TIME_IN_PROGRESS = 54460
34
34
  DBMS_MIN_PROTOCOL_VERSION_WITH_PASSWORD_COMPLEXITY_RULES = 54461
35
35
  DBMS_MIN_REVISION_WITH_INTERSERVER_SECRET_V2 = 54462
36
+ DBMS_MIN_PROTOCOL_VERSION_WITH_TOTAL_BYTES_IN_PROGRESS = 54463
37
+ DBMS_MIN_PROTOCOL_VERSION_WITH_TIMEZONE_UPDATES = 54464
38
+ DBMS_MIN_REVISION_WITH_SYSTEM_KEYWORDS_TABLE = 54468
36
39
 
37
40
  # Timeouts
38
41
  DBMS_DEFAULT_CONNECT_TIMEOUT_SEC = 10
@@ -48,7 +51,7 @@ CLIENT_NAME = 'python-driver'
48
51
  CLIENT_VERSION_MAJOR = 20
49
52
  CLIENT_VERSION_MINOR = 10
50
53
  CLIENT_VERSION_PATCH = 2
51
- CLIENT_REVISION = DBMS_MIN_REVISION_WITH_INTERSERVER_SECRET_V2
54
+ CLIENT_REVISION = DBMS_MIN_REVISION_WITH_SYSTEM_KEYWORDS_TABLE
52
55
 
53
56
  BUFFER_SIZE = 1048576
54
57
 
@@ -1,13 +1,16 @@
1
1
  import numpy as np
2
2
  import pandas as pd
3
+ from pandas.core.arrays import ExtensionArray
3
4
 
4
5
 
5
6
  def column_chunks(columns, n):
6
7
  for column in columns:
7
- if not isinstance(column, (np.ndarray, pd.DatetimeIndex)):
8
+ if not isinstance(
9
+ column, (np.ndarray, pd.DatetimeIndex, ExtensionArray)
10
+ ):
8
11
  raise TypeError(
9
12
  'Unsupported column type: {}. '
10
- 'ndarray/DatetimeIndex is expected.'
13
+ 'ndarray/DatetimeIndex/ExtensionArray is expected.'
11
14
  .format(type(column))
12
15
  )
13
16
 
@@ -6,7 +6,8 @@ class Progress(object):
6
6
  def __init__(self):
7
7
  self.rows = 0
8
8
  self.bytes = 0
9
- self.total_rows = 0
9
+ self.total_rows = 0 # total_rows_to_read
10
+ self.total_bytes = 0 # total_bytes_to_read
10
11
  self.written_rows = 0
11
12
  self.written_bytes = 0
12
13
  self.elapsed_ns = 0
@@ -21,6 +22,10 @@ class Progress(object):
21
22
  if revision >= defines.DBMS_MIN_REVISION_WITH_TOTAL_ROWS_IN_PROGRESS:
22
23
  self.total_rows = read_varint(fin)
23
24
 
25
+ if revision >= defines. \
26
+ DBMS_MIN_PROTOCOL_VERSION_WITH_TOTAL_BYTES_IN_PROGRESS:
27
+ self.total_bytes = read_varint(fin)
28
+
24
29
  if revision >= defines.DBMS_MIN_REVISION_WITH_CLIENT_WRITE_INFO:
25
30
  self.written_rows = read_varint(fin)
26
31
  self.written_bytes = read_varint(fin)
@@ -33,6 +38,7 @@ class Progress(object):
33
38
  self.rows += another_progress.rows
34
39
  self.bytes += another_progress.bytes
35
40
  self.total_rows += another_progress.total_rows
41
+ self.total_bytes += another_progress.total_bytes
36
42
  self.written_rows += another_progress.written_rows
37
43
  self.written_bytes += another_progress.written_bytes
38
44
  self.elapsed_ns += another_progress.elapsed_ns
@@ -29,7 +29,10 @@ class ClientPacketTypes(object):
29
29
 
30
30
  @classmethod
31
31
  def to_str(cls, packet):
32
- return 'Unknown packet' if packet > 5 else cls._types_str[packet]
32
+ try:
33
+ return cls._types_str[packet]
34
+ except IndexError:
35
+ return 'Unknown packet'
33
36
 
34
37
 
35
38
  class ServerPacketTypes(object):
@@ -81,15 +84,28 @@ class ServerPacketTypes(object):
81
84
  # Packet with profile events from server.
82
85
  PROFILE_EVENTS = 14
83
86
 
87
+ MERGE_TREE_ALL_RANGES_ANNOUNCEMENT = 15
88
+
89
+ # Request from a MergeTree replica to a coordinator
90
+ MERGE_TREE_READ_TASK_REQUEST = 16
91
+
92
+ # Receive server's (session-wide) default timezone
93
+ TIMEZONE_UPDATE = 17
94
+
84
95
  _types_str = [
85
96
  'Hello', 'Data', 'Exception', 'Progress', 'Pong', 'EndOfStream',
86
97
  'ProfileInfo', 'Totals', 'Extremes', 'TablesStatusResponse', 'Log',
87
- 'TableColumns', 'PartUUIDs', 'ReadTaskRequest', 'ProfileEvents'
98
+ 'TableColumns', 'PartUUIDs', 'ReadTaskRequest', 'ProfileEvents',
99
+ 'MergeTreeAllRangesAnnouncement', 'MergeTreeReadTaskRequest',
100
+ 'TimezoneUpdate'
88
101
  ]
89
102
 
90
103
  @classmethod
91
104
  def to_str(cls, packet):
92
- return 'Unknown packet' if packet > 14 else cls._types_str[packet]
105
+ try:
106
+ return cls._types_str[packet]
107
+ except IndexError:
108
+ return 'Unknown packet'
93
109
 
94
110
  @classmethod
95
111
  def strings_in_message(cls, packet):
@@ -1,3 +1,5 @@
1
+ import logging
2
+
1
3
  from ..block import ColumnOrientedBlock, BlockInfo
2
4
  from ..columns.service import read_column, write_column
3
5
  from ..reader import read_binary_str, read_binary_uint8
@@ -5,6 +7,8 @@ from ..varint import write_varint, read_varint
5
7
  from ..writer import write_binary_str, write_binary_uint8
6
8
  from .. import defines
7
9
 
10
+ logger = logging.getLogger(__name__)
11
+
8
12
 
9
13
  class BlockOutputStream(object):
10
14
  def __init__(self, fout, context):
@@ -40,6 +44,7 @@ class BlockOutputStream(object):
40
44
  # We write always sparse data without custom serialization.
41
45
  write_binary_uint8(0, self.fout)
42
46
 
47
+ logger.debug('Writing column %s', col_name)
43
48
  write_column(self.context, col_name, col_type, items,
44
49
  self.fout, types_check=block.types_check)
45
50
 
@@ -80,6 +85,7 @@ class BlockInputStream(object):
80
85
  has_custom_serialization = bool(read_binary_uint8(self.fin))
81
86
 
82
87
  if n_rows:
88
+ logger.debug('Reading column %s', column_name)
83
89
  column = read_column(
84
90
  self.context, column_type, n_rows,
85
91
  self.fin, use_numpy=use_numpy,
@@ -21,7 +21,7 @@ escape_chars_map = {
21
21
 
22
22
 
23
23
  def escape_datetime(item, context):
24
- server_tz = timezone(context.server_info.timezone)
24
+ server_tz = timezone(context.server_info.get_timezone())
25
25
 
26
26
  if item.tzinfo is not None:
27
27
  item = item.astimezone(server_tz)
@@ -1,4 +1,6 @@
1
+ import ssl
1
2
  from itertools import islice, tee
3
+ from urllib.parse import urlparse, parse_qs, unquote
2
4
 
3
5
 
4
6
  def chunks(seq, n):
@@ -55,3 +57,117 @@ def asbool(obj):
55
57
  else:
56
58
  raise ValueError('String is not true/false: %r' % obj)
57
59
  return bool(obj)
60
+
61
+
62
+ def parse_url(url):
63
+ """
64
+ Parses url into host and kwargs suitable for further Client construction.
65
+ Return host and kwargs.
66
+
67
+ For example::
68
+
69
+ clickhouse://[user:password]@localhost:9000/default
70
+ clickhouses://[user:password]@localhost:9440/default
71
+
72
+ Three URL schemes are supported:
73
+
74
+ * clickhouse:// creates a normal TCP socket connection
75
+ * clickhouses:// creates a SSL wrapped TCP socket connection
76
+
77
+ Any additional querystring arguments will be passed along to
78
+ the Connection class's initializer.
79
+ """
80
+ url = urlparse(url)
81
+
82
+ settings = {}
83
+ kwargs = {}
84
+
85
+ host = url.hostname
86
+
87
+ if url.port is not None:
88
+ kwargs['port'] = url.port
89
+
90
+ path = url.path.replace('/', '', 1)
91
+ if path:
92
+ kwargs['database'] = path
93
+
94
+ if url.username is not None:
95
+ kwargs['user'] = unquote(url.username)
96
+
97
+ if url.password is not None:
98
+ kwargs['password'] = unquote(url.password)
99
+
100
+ if url.scheme == 'clickhouses':
101
+ kwargs['secure'] = True
102
+
103
+ compression_algs = {'lz4', 'lz4hc', 'zstd'}
104
+ timeouts = {
105
+ 'connect_timeout',
106
+ 'send_receive_timeout',
107
+ 'sync_request_timeout'
108
+ }
109
+
110
+ for name, value in parse_qs(url.query).items():
111
+ if not value or not len(value):
112
+ continue
113
+
114
+ value = value[0]
115
+
116
+ if name == 'compression':
117
+ value = value.lower()
118
+ if value in compression_algs:
119
+ kwargs[name] = value
120
+ else:
121
+ kwargs[name] = asbool(value)
122
+
123
+ elif name == 'secure':
124
+ kwargs[name] = asbool(value)
125
+
126
+ elif name == 'use_numpy':
127
+ settings[name] = asbool(value)
128
+
129
+ elif name == 'round_robin':
130
+ kwargs[name] = asbool(value)
131
+
132
+ elif name == 'client_name':
133
+ kwargs[name] = value
134
+
135
+ elif name in timeouts:
136
+ kwargs[name] = float(value)
137
+
138
+ elif name == 'compress_block_size':
139
+ kwargs[name] = int(value)
140
+
141
+ elif name == 'settings_is_important':
142
+ kwargs[name] = asbool(value)
143
+
144
+ elif name == 'tcp_keepalive':
145
+ try:
146
+ kwargs[name] = asbool(value)
147
+ except ValueError:
148
+ parts = value.split(',')
149
+ kwargs[name] = (
150
+ int(parts[0]), int(parts[1]), int(parts[2])
151
+ )
152
+ elif name == 'client_revision':
153
+ kwargs[name] = int(value)
154
+
155
+ # ssl
156
+ elif name == 'verify':
157
+ kwargs[name] = asbool(value)
158
+ elif name == 'check_hostname':
159
+ kwargs[name] = asbool(value)
160
+ elif name == 'ssl_version':
161
+ kwargs[name] = getattr(ssl, value)
162
+ elif name in ['ca_certs', 'ciphers', 'keyfile', 'keypass', 'certfile',
163
+ 'server_hostname']:
164
+ kwargs[name] = value
165
+ elif name == 'alt_hosts':
166
+ kwargs['alt_hosts'] = value
167
+ else:
168
+ settings[name] = value
169
+
170
+ if settings:
171
+ kwargs['settings'] = settings
172
+
173
+ return host, kwargs
Binary file
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: clickhouse-driver
3
- Version: 0.2.8
3
+ Version: 0.2.10
4
4
  Summary: Python driver with native interface for ClickHouse
5
5
  Home-page: https://github.com/mymarilyn/clickhouse-driver
6
6
  Author: Konstantin Lebedev
@@ -17,11 +17,12 @@ Classifier: License :: OSI Approved :: MIT License
17
17
  Classifier: Operating System :: OS Independent
18
18
  Classifier: Programming Language :: SQL
19
19
  Classifier: Programming Language :: Python :: 3
20
- Classifier: Programming Language :: Python :: 3.7
21
- Classifier: Programming Language :: Python :: 3.8
22
20
  Classifier: Programming Language :: Python :: 3.9
23
21
  Classifier: Programming Language :: Python :: 3.10
24
22
  Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Classifier: Programming Language :: Python :: 3.14
25
26
  Classifier: Programming Language :: Python :: Implementation :: PyPy
26
27
  Classifier: Topic :: Database
27
28
  Classifier: Topic :: Software Development
@@ -29,20 +30,33 @@ Classifier: Topic :: Software Development :: Libraries
29
30
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
30
31
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
31
32
  Classifier: Topic :: Scientific/Engineering :: Information Analysis
32
- Requires-Python: >=3.7, <4
33
+ Requires-Python: >=3.9, <4
33
34
  License-File: LICENSE
34
35
  Requires-Dist: pytz
35
36
  Requires-Dist: tzlocal
36
37
  Provides-Extra: lz4
37
- Requires-Dist: clickhouse-cityhash >=1.0.2.1 ; extra == 'lz4'
38
- Requires-Dist: lz4 ; (implementation_name != "pypy") and extra == 'lz4'
39
- Requires-Dist: lz4 <=3.0.1 ; (implementation_name == "pypy") and extra == 'lz4'
40
- Provides-Extra: numpy
41
- Requires-Dist: numpy >=1.12.0 ; extra == 'numpy'
42
- Requires-Dist: pandas >=0.24.0 ; extra == 'numpy'
38
+ Requires-Dist: lz4<=3.0.1; implementation_name == "pypy" and extra == "lz4"
39
+ Requires-Dist: lz4; implementation_name != "pypy" and extra == "lz4"
40
+ Requires-Dist: clickhouse-cityhash>=1.0.2.1; extra == "lz4"
43
41
  Provides-Extra: zstd
44
- Requires-Dist: zstd ; extra == 'zstd'
45
- Requires-Dist: clickhouse-cityhash >=1.0.2.1 ; extra == 'zstd'
42
+ Requires-Dist: zstd; extra == "zstd"
43
+ Requires-Dist: clickhouse-cityhash>=1.0.2.1; extra == "zstd"
44
+ Provides-Extra: numpy
45
+ Requires-Dist: numpy>=1.12.0; extra == "numpy"
46
+ Requires-Dist: pandas>=0.24.0; extra == "numpy"
47
+ Dynamic: author
48
+ Dynamic: author-email
49
+ Dynamic: classifier
50
+ Dynamic: description
51
+ Dynamic: home-page
52
+ Dynamic: keywords
53
+ Dynamic: license
54
+ Dynamic: license-file
55
+ Dynamic: project-url
56
+ Dynamic: provides-extra
57
+ Dynamic: requires-dist
58
+ Dynamic: requires-python
59
+ Dynamic: summary
46
60
 
47
61
  ClickHouse Python Driver
48
62
  ========================
@@ -1,39 +1,39 @@
1
- clickhouse_driver/__init__.py,sha256=OmAov0f6GIBaEKyG5T6rvrDLSL4q1Ysnetjt3iEsMZg,158
1
+ clickhouse_driver/__init__.py,sha256=-RHHLH8KESN2fiVtQ1br8WM8Hl6CkqkunzvCOJCNkbM,159
2
2
  clickhouse_driver/block.py,sha256=t2zZhtCzwp4aJj0CuDqXlDEgHXgwc6GRsIks2aaIZvI,6311
3
3
  clickhouse_driver/blockstreamprofileinfo.py,sha256=nYx9QXWAS8aPiRbtAzwLRT5JIlJ8S103JMkudncwpFg,712
4
- clickhouse_driver/bufferedreader.cp312-win32.pyd,sha256=RmJgKsXzvPwUlflnuPi7rJwsgPzZp3nZ50cRszfywrA,87552
5
- clickhouse_driver/bufferedwriter.cp312-win32.pyd,sha256=juFtqHO9FHQV8yA-jagPjZAy0Mjer9YhbujE0H2R8Es,86528
6
- clickhouse_driver/client.py,sha256=aMjWyS98BqonoEuZxNC_q4DdUjBf8vADLAAcrkJPmKA,34703
7
- clickhouse_driver/clientinfo.py,sha256=Tw3NbuDHglT6_f0AcT0IdhqW_hVePtzh9pypx5nk5ZE,4260
8
- clickhouse_driver/connection.py,sha256=Ol9ZljjN63P840yrUlWPl5g9lRldh9XTD5fVOiloYi8,28403
4
+ clickhouse_driver/bufferedreader.cp312-win32.pyd,sha256=JklvZWSinVBCtwZ4II-Cte7d_NfmHsFTxdyy08HoRiY,74240
5
+ clickhouse_driver/bufferedwriter.cp312-win32.pyd,sha256=sJkk4YeZ24in30X_odp4e4HwyFIYVhzB-7UI_neD5J4,74752
6
+ clickhouse_driver/client.py,sha256=iYSz2DLQC-t6RLmtWJvtHoEWBAFtKDIafl874tz2ebs,32251
7
+ clickhouse_driver/clientinfo.py,sha256=cdMA2td6tuYPsN6m4qtGzLzGKY4zQune0s0fZlNRiEA,4271
8
+ clickhouse_driver/connection.py,sha256=1GlehPjkzAPtFaAAzKHlBxyXMAHLyNOtNUKQjG7kjq4,29950
9
9
  clickhouse_driver/context.py,sha256=yPkJ_BN4LGODvKCOjNlDGqABwxAMQTQl7w1vIGRPBDg,909
10
- clickhouse_driver/defines.py,sha256=hXIR10BvH_sxUN35pk-f-VeIbM7CA0Ll2B2yNuAy10A,1958
10
+ clickhouse_driver/defines.py,sha256=EwrQz7sNNUmGZmZywQFpDpBFc67nB9GAZOBQEvvUJzA,2130
11
11
  clickhouse_driver/errors.py,sha256=mffBg-Y2LFOSRzvE9V34RwFcvmfh67yqLeEBhrdjeXE,14343
12
12
  clickhouse_driver/log.py,sha256=P9VS_YDY3a4JQsNOeUNNK0L1AAallTC1GF4FG5gCrac,1091
13
13
  clickhouse_driver/opentelemetry.py,sha256=GIzHCxdB8LB1ozXtUjIpxsdznmRABAts64G2u2l4C_A,1622
14
- clickhouse_driver/progress.py,sha256=MagDPiLzWG-2Arecu5O6JlK98242ayt9yPa9H7v9o5k,1288
15
- clickhouse_driver/protocol.py,sha256=ZRJuawN_v8wXFrHrcpSiBqu7idKsPd3tQnCcDuTlIw8,2561
14
+ clickhouse_driver/progress.py,sha256=mom2NRLg8dfE_ObOEthKqjSBIliBvDjt7joePvY_Fdw,1574
15
+ clickhouse_driver/protocol.py,sha256=BxCJiZLH0QkFmdNwog1OJm9MEMwdPZ1i5Jv1_NHcLb0,2965
16
16
  clickhouse_driver/queryprocessingstage.py,sha256=lbV-bB5jWUp0hqYBTsUcRYkJhCgCTfN2G21AykMoAp0,186
17
17
  clickhouse_driver/reader.py,sha256=PWKOKwjszu0sJbG-dd_TyGn8tQAzxg2gCJVbz27G3-8,1322
18
18
  clickhouse_driver/readhelpers.py,sha256=tXOmSL9GSeHLdKE2yBlk0epfy-hGJ3bTOLq3Q6sXIkw,717
19
19
  clickhouse_driver/result.py,sha256=RpjBUvRQJ70xISye7u6gxgQBJwc2UkNuusLkaZF7SmM,4098
20
- clickhouse_driver/varint.cp312-win32.pyd,sha256=fjZmM5lZyqpvBZ4XLNG9hMYo5CXFqIHDG6tlI1vrUmU,34304
20
+ clickhouse_driver/varint.cp312-win32.pyd,sha256=FF4eJeVbjqnrNtCae4bBjDtWRGeX08hqTyyTjcA-KQQ,32768
21
21
  clickhouse_driver/writer.py,sha256=yf5f1vTr56YFtL-Swpi_jBsPw5bQS56j53joB0Acdyk,1286
22
22
  clickhouse_driver/columns/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
23
  clickhouse_driver/columns/arraycolumn.py,sha256=UiA48jw3o4-ert8BnzEbl2h1jVmtDH3jtcP1BTPru2w,5322
24
24
  clickhouse_driver/columns/base.py,sha256=jRThD8Ol7gwNy3x0M7vvslkm0RDOsCLJqKGf0arsa0w,6504
25
25
  clickhouse_driver/columns/boolcolumn.py,sha256=QgVCqbZWoFD1yvpppc92iiXIzReCVEh692efNSNH4UE,127
26
26
  clickhouse_driver/columns/datecolumn.py,sha256=FT6qzmniFGKw5SpjFITiBf-fJGEwaoUIz7PMZMFxe8M,3209
27
- clickhouse_driver/columns/datetimecolumn.py,sha256=pO8g1exACp6EfUO0prra5X3T_gg1MJ8bSoi5Rt2Vxjg,6691
27
+ clickhouse_driver/columns/datetimecolumn.py,sha256=RWucoNtcc4wUGQ6xPL0cTNmxFy8eQ50ZdHzexXBm3XE,6730
28
28
  clickhouse_driver/columns/decimalcolumn.py,sha256=MBczjN6AtI0jgbVTNguwVE7ZdWql0XuMl-QohxV-sN0,3450
29
- clickhouse_driver/columns/enumcolumn.py,sha256=GCSW-fjtJC4_5YKo9oKtG8GT9UhLcRWCjrBVhEAUp-A,3219
29
+ clickhouse_driver/columns/enumcolumn.py,sha256=zqZkSVVXfWsaPUbP59ayy7w8rG4otS9J51UPD29zs2Y,3674
30
30
  clickhouse_driver/columns/exceptions.py,sha256=vrZMZ9ORRP7RSSd34azM_xFaeMaaYbwy6h6ZCeFNbIg,163
31
31
  clickhouse_driver/columns/floatcolumn.py,sha256=Mci7Fl05dgkpDQRfjGaDP9QraZER5nkYX1lVsTA89Yk,925
32
32
  clickhouse_driver/columns/intcolumn.py,sha256=ntpxyMiu44VQYJ0HyHlglwxMgUtnIgf3JE9LUD9Xwwc,3519
33
33
  clickhouse_driver/columns/intervalcolumn.py,sha256=oQeybb4qrcp07LeVYfqgeZuHYGH5RaY7KnWVoIsDe1I,600
34
34
  clickhouse_driver/columns/ipcolumn.py,sha256=LIt-NiUy70-8u5Amu0v0tlAlrolZ8gXzlFxcJYmjRAk,4095
35
35
  clickhouse_driver/columns/jsoncolumn.py,sha256=SeCLSrmRoEg9wlvoehs7-EmrV8XkOL3lknzeOORhbJg,1175
36
- clickhouse_driver/columns/largeint.cp312-win32.pyd,sha256=WPxFFDlRVb3H0tP5j19djieoXjvQNBmxR_mlz0CrNII,56320
36
+ clickhouse_driver/columns/largeint.cp312-win32.pyd,sha256=cWp3sM8WzTGfr5hRqQtus2X_BIF4ddL67pgAbjm6FBs,55296
37
37
  clickhouse_driver/columns/lowcardinalitycolumn.py,sha256=vMlhYYUPNmFhQiUAS77pE25KjI8TrO6ySz6w0ByBdZQ,4920
38
38
  clickhouse_driver/columns/mapcolumn.py,sha256=gX7xvwsilfOHDCwmRJu4fW2Y1OrN_1cgZTmbvcyer30,2133
39
39
  clickhouse_driver/columns/nestedcolumn.py,sha256=7x3xNYR22kEmgV4_3rPVZea2zCTU364O1tlOewMgw3M,303
@@ -44,20 +44,20 @@ clickhouse_driver/columns/service.py,sha256=6nIGwvcG-5LErQFn_3a2GRKZI2OnKYsy2oII
44
44
  clickhouse_driver/columns/simpleaggregatefunctioncolumn.py,sha256=zDWBd_Hc7AktHFZ5JY7SwxIk7G4WBHdJdcPBqihe7q0,235
45
45
  clickhouse_driver/columns/stringcolumn.py,sha256=C-l7HaReg9AcJGQvgpSH07aV-CZMBP1jKArPHZi8Q2k,2059
46
46
  clickhouse_driver/columns/tuplecolumn.py,sha256=DnwtEwVXzm-YyL5-cPe9MiOLOoTEcDhcYeBY6SuuZZQ,2040
47
- clickhouse_driver/columns/util.py,sha256=5DRGBsWSTldqZRZJ53J85zh_jepkwkAsYXhAeDJQW4I,1395
47
+ clickhouse_driver/columns/util.py,sha256=3q965GjEmKkTmjIva31PhvAEGfuuGulad1amK7kQBEQ,1427
48
48
  clickhouse_driver/columns/uuidcolumn.py,sha256=7CLPxsHJtV6Zt9jxILSkE0LAwffloLmrjjbwCZH34mA,1865
49
49
  clickhouse_driver/columns/numpy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
50
  clickhouse_driver/columns/numpy/base.py,sha256=Wk1ADpoQGWQv5X8sWi0esA2jmQGEoDakXZxImuYM2eM,1410
51
51
  clickhouse_driver/columns/numpy/boolcolumn.py,sha256=0_RwEroY2-ZqMuxKeTNG27ZCWN_y9BxYZzbuFKeqoQg,140
52
52
  clickhouse_driver/columns/numpy/datecolumn.py,sha256=xWFsFwiRmszK9A20UWnF4beTE5f8XQDj18x53xtXtSI,482
53
- clickhouse_driver/columns/numpy/datetimecolumn.py,sha256=XQ3JsxVdAvWJndNek4-i07JO2QJIdUnJQWzOxvF3PMc,4777
53
+ clickhouse_driver/columns/numpy/datetimecolumn.py,sha256=X6XgslCvWOF3dtVP3ogDQ4WLWhQqAkAdw5qAIuE8f1o,4890
54
54
  clickhouse_driver/columns/numpy/floatcolumn.py,sha256=WnnlyR3vabh6ixllXwdyQ_t8y5MSqCddImWityxz6pE,599
55
55
  clickhouse_driver/columns/numpy/intcolumn.py,sha256=ZzmjvjpEaZ_kk-mGEPdBAqUCjqCgGT8tUEzbQKwUVVA,792
56
56
  clickhouse_driver/columns/numpy/lowcardinalitycolumn.py,sha256=ExYtwBBn6WfSQtWC0f79ZqjdqO3ZJ3IL6KtlQOgMh-k,3368
57
57
  clickhouse_driver/columns/numpy/service.py,sha256=ENk26HPfxtbumcO_b2fGTNJdTgaJT0a-l3q_xrRIZZ4,2126
58
58
  clickhouse_driver/columns/numpy/stringcolumn.py,sha256=FH87XPQv3ga0ZJnngubqu4DgxmUt8bjW8xJcNfOU7EI,2439
59
59
  clickhouse_driver/columns/numpy/tuplecolumn.py,sha256=5RqhCIZ-CrWy_D0yyfTmlWBJgrunT5Yk-bM4OvIKZ1o,1233
60
- clickhouse_driver/compression/__init__.py,sha256=_XcKSkvWWPvEX4jeefYX1Alee58w6ktFOXrsIFRko_0,714
60
+ clickhouse_driver/compression/__init__.py,sha256=7D3Wk_-KQsnIdaqp9163YCDsQ98Stbl6GF1h1KAFgAg,840
61
61
  clickhouse_driver/compression/base.py,sha256=MCTK2aOGT3Lkso5r3-U-ilj2ln4r-nnfNI_2Ostsu6Q,2434
62
62
  clickhouse_driver/compression/lz4.py,sha256=ZTf1zn0uG26O29XHBBZeve26JLPNbbEWWMZe065oa0Y,631
63
63
  clickhouse_driver/compression/lz4hc.py,sha256=zGbj9vnM20W298kgzD0dIAgIeV30e5Kx8NSN8t4SDas,195
@@ -69,7 +69,7 @@ clickhouse_driver/dbapi/errors.py,sha256=yv2SDIK-ZlWmIxBoF5_igfHRyh9zTUJSgAj9g8S
69
69
  clickhouse_driver/dbapi/extras.py,sha256=je1tjk6gb-QqLMoWu_iXCqhJK159vyyHsCB_8vXkXyw,2129
70
70
  clickhouse_driver/numpy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
71
  clickhouse_driver/numpy/block.py,sha256=k8CqQxaVSviZvicS03exPZG_LFwbzB5-NnLB2EuBKgQ,180
72
- clickhouse_driver/numpy/helpers.py,sha256=jKiZ3GOp1G5Q2Y-6S_YG9t9RtdSLa-AH1grz71OWyS0,704
72
+ clickhouse_driver/numpy/helpers.py,sha256=cwRvBX4a-o-QdccYIxFT3p8DPUaXqyXWsVNTSiG8Cdw,803
73
73
  clickhouse_driver/numpy/result.py,sha256=D-7IsaIt0u4WJ8iSzXYBB0OiPgPu5ptu-1HiJvTW-No,3563
74
74
  clickhouse_driver/settings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
75
  clickhouse_driver/settings/available.py,sha256=8b1YUjt-_6gO9Cio02VZSMPdhM70zk-NMhE4f_jwFx8,16613
@@ -77,13 +77,13 @@ clickhouse_driver/settings/types.py,sha256=u_A3FzREqIjj4OSEdKUwm1Xn8jEnGpp-9Avqb
77
77
  clickhouse_driver/settings/writer.py,sha256=x0FVdbKZR9lN-hXAT2xxkoqvGo1ojvWWwyP3NaI7ZDA,1099
78
78
  clickhouse_driver/streams/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
79
79
  clickhouse_driver/streams/compressed.py,sha256=eyFCuq_sLX6oJLQ_h1QX3IB9IgUx56pPqPfhmeiA6ag,2865
80
- clickhouse_driver/streams/native.py,sha256=2qAs5deUU4NwLG3zQAyxNwGsKmJO41-qlTRKGRFSAJA,3316
80
+ clickhouse_driver/streams/native.py,sha256=lIJFxLRFrh2zqRAvf8W2JPmXdGtJu3LPDUkfaBrLAfk,3493
81
81
  clickhouse_driver/util/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
82
82
  clickhouse_driver/util/compat.py,sha256=wIB_6ULSapKx-Fi64lZ0YJ-TtWOQXePHE90wZopIM1I,876
83
- clickhouse_driver/util/escape.py,sha256=4RPfNzC5IvatoZB9llmJM4436XArCXQDEqT_cqdwCBk,2256
84
- clickhouse_driver/util/helpers.py,sha256=1oLkkkILXZnEbdjAe2Ags-Y-T-Bq9rk94W6edh-1ZLU,1448
85
- clickhouse_driver-0.2.8.dist-info/LICENSE,sha256=b2SjNa4zGQk6XzJwmeE1dQjjtWRvX_zxJIAl_ZbN_S8,1143
86
- clickhouse_driver-0.2.8.dist-info/METADATA,sha256=nmFh97sS6TF5P2tzzmvKfO-HUNGxXWzyrFwC4D6OkuU,6290
87
- clickhouse_driver-0.2.8.dist-info/WHEEL,sha256=4N7jptPh4OYjkcvBzbL_SyiQfKWhgnHPSC8E7zx3zFw,98
88
- clickhouse_driver-0.2.8.dist-info/top_level.txt,sha256=PrE0Lrs4d-gRQwzABaABfym9Qvr4nN6uTrDy6Q7zQME,18
89
- clickhouse_driver-0.2.8.dist-info/RECORD,,
83
+ clickhouse_driver/util/escape.py,sha256=vL8sY6XdHhjFwtHn8yQ3JbeZfocWt6jyvxFf-nEiRtM,2262
84
+ clickhouse_driver/util/helpers.py,sha256=IGmaK3L8_i8C-3NJWPWT2VK5hAtLxfflF0ti2z45SHs,4574
85
+ clickhouse_driver-0.2.10.dist-info/licenses/LICENSE,sha256=b2SjNa4zGQk6XzJwmeE1dQjjtWRvX_zxJIAl_ZbN_S8,1143
86
+ clickhouse_driver-0.2.10.dist-info/METADATA,sha256=L7JSolHa_2zc_NGUpJK-fXouxcU8UNJseMQkSxePWYE,6607
87
+ clickhouse_driver-0.2.10.dist-info/WHEEL,sha256=LwxTQZ0gyDP_uaeNCLm-ZIktY9hv6x0e22Q-hgFd-po,97
88
+ clickhouse_driver-0.2.10.dist-info/top_level.txt,sha256=PrE0Lrs4d-gRQwzABaABfym9Qvr4nN6uTrDy6Q7zQME,18
89
+ clickhouse_driver-0.2.10.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.41.2)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: false
4
4
  Tag: cp312-cp312-win32
5
5