Skip to content

Commit 2cc2515

Browse files
committed
Support [clickhouse-driver 0.2.6](https://github.com/mymarilyn/clickhouse-driver) #14, drop support for python3.6.
1 parent 109b89b commit 2cc2515

File tree

10 files changed

+85
-50
lines changed

10 files changed

+85
-50
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
### 1.0.3
2+
- Fix reading settings in explain, pull request [#13](https://github.com/jayvynl/django-clickhouse-backend/pull/13) by [mahdi-jfri](https://github.com/mahdi-jfri).
3+
- Add toYYYYMM[DD[hhmmss]] functions.
4+
- Fix str(queryset.query) when default database is not clickhouse.
5+
- Fix [bug when save django model instance](https://github.com/jayvynl/django-clickhouse-backend/issues/9).
6+
- Support [clickhouse-driver 0.2.6](https://github.com/mymarilyn/clickhouse-driver), drop support for python3.6.
7+
18
### 1.0.2 (2023-02-28)
29
- Fix test db name when NAME not provided in DATABASES setting.
310
- Fix Enum error when provided an IntegerChoices value.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Read [Documentation](https://github.com/jayvynl/django-clickhouse-backend/blob/m
3131

3232
**Requirements:**
3333

34-
- [Python](https://www.python.org/) >= 3.6
34+
- [Python](https://www.python.org/) >= 3.7
3535
- [Django](https://docs.djangoproject.com/) >= 3.2
3636
- [clickhouse driver](https://github.com/mymarilyn/clickhouse-driver)
3737
- [clickhouse pool](https://github.com/ericmccarthy7/clickhouse-pool)

clickhouse_backend/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.0.2
1+
1.0.3

clickhouse_backend/driver/__init__.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@
99

1010
from .connection import Connection
1111
# Binary is compatible for django's BinaryField.
12-
from .types import ( # NOQA
13-
Binary
14-
)
12+
from .types import Binary # NOQA
1513

1614

1715
def connect(dsn=None, host=None,

clickhouse_backend/driver/connection.py

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,70 @@
11
import re
2+
from typing import Dict
23

3-
from clickhouse_driver.dbapi import connection
4+
from clickhouse_driver import connection
5+
from clickhouse_driver.dbapi import connection as dbapi_connection
46
from clickhouse_driver.dbapi import cursor
57
from clickhouse_driver.dbapi import errors
68
from clickhouse_pool.pool import ChPoolError
79

10+
from .escape import escape_params
811
from .pool import ClickhousePool
912

1013
update_pattern = re.compile(r'\s*alter\s+table\s+(.+)\s+update.+where\s+(.+)', flags=re.IGNORECASE)
1114

1215

16+
def send_query(self, query, query_id=None, params=None):
17+
if not self.connected:
18+
self.connect()
19+
20+
connection.write_varint(connection.ClientPacketTypes.QUERY, self.fout)
21+
22+
connection.write_binary_str(query_id or '', self.fout)
23+
24+
revision = self.server_info.used_revision
25+
if revision >= connection.defines.DBMS_MIN_REVISION_WITH_CLIENT_INFO:
26+
client_info = connection.ClientInfo(self.client_name, self.context,
27+
client_revision=self.client_revision)
28+
client_info.query_kind = connection.ClientInfo.QueryKind.INITIAL_QUERY
29+
30+
client_info.write(revision, self.fout)
31+
32+
settings_as_strings = (
33+
revision >= connection.defines
34+
.DBMS_MIN_REVISION_WITH_SETTINGS_SERIALIZED_AS_STRINGS
35+
)
36+
settings_flags = 0
37+
if self.settings_is_important:
38+
settings_flags |= connection.SettingsFlags.IMPORTANT
39+
connection.write_settings(self.context.settings, self.fout,
40+
settings_as_strings, settings_flags)
41+
42+
if revision >= connection.defines.DBMS_MIN_REVISION_WITH_INTERSERVER_SECRET:
43+
connection.write_binary_str('', self.fout)
44+
45+
connection.write_varint(connection.QueryProcessingStage.COMPLETE, self.fout)
46+
connection.write_varint(self.compression, self.fout)
47+
48+
connection.write_binary_str(query, self.fout)
49+
50+
if revision >= connection.defines.DBMS_MIN_PROTOCOL_VERSION_WITH_PARAMETERS:
51+
if not isinstance(params, Dict):
52+
params = None
53+
# Always settings_as_strings = True
54+
escaped = escape_params(
55+
params or {}, self.context, for_server=True
56+
)
57+
connection.write_settings(escaped, self.fout, True, connection.SettingsFlags.CUSTOM)
58+
59+
connection.logger.debug('Query: %s', query)
60+
61+
self.fout.flush()
62+
63+
64+
# Monkey patch to resolve https://github.com/jayvynl/django-clickhouse-backend/issues/14
65+
connection.Connection.send_query = send_query
66+
67+
1368
class Cursor(cursor.Cursor):
1469
def close(self):
1570
"""Push client back to connection pool"""
@@ -46,7 +101,7 @@ def execute(self, operation, parameters=None):
46101
super().execute(operation, parameters)
47102

48103

49-
class Connection(connection.Connection):
104+
class Connection(dbapi_connection.Connection):
50105
"""Connection class with support for connection pool."""
51106
def __init__(self, *args, **kwargs):
52107
super().__init__(*args, **kwargs)

clickhouse_backend/driver/escape.py

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from datetime import date, datetime, timezone
1+
from datetime import date, datetime, time, timezone
22
from enum import Enum
33
from ipaddress import IPv4Address, IPv6Address
44
from typing import Sequence, Dict, Union
@@ -37,7 +37,8 @@ def escape_binary(item: bytes, context):
3737
return b2s[1:]
3838

3939

40-
def escape_param(item, context):
40+
@escape.maybe_enquote_for_server
41+
def escape_param(item, context, for_server=False):
4142
if item is None:
4243
return 'NULL'
4344

@@ -47,17 +48,23 @@ def escape_param(item, context):
4748
elif isinstance(item, date):
4849
return "'%s'" % item.strftime('%Y-%m-%d')
4950

51+
elif isinstance(item, time):
52+
return "'%s'" % item.strftime('%H:%M:%S')
53+
5054
elif isinstance(item, str):
55+
# We need double escaping for server-side parameters.
56+
if for_server:
57+
item = ''.join(escape.escape_chars_map.get(c, c) for c in item)
5158
return "'%s'" % ''.join(escape.escape_chars_map.get(c, c) for c in item)
5259

5360
elif isinstance(item, list):
54-
return "[%s]" % ', '.join(str(escape_param(x, context)) for x in item)
61+
return "[%s]" % ', '.join(str(escape_param(x, context, for_server=for_server)) for x in item)
5562

5663
elif isinstance(item, tuple):
57-
return "(%s)" % ', '.join(str(escape_param(x, context)) for x in item)
64+
return "(%s)" % ', '.join(str(escape_param(x, context, for_server=for_server)) for x in item)
5865

5966
elif isinstance(item, Enum):
60-
return escape_param(item.value, context)
67+
return escape_param(item.value, context, for_server=for_server)
6168

6269
elif isinstance(item, (UUID, IPv4Address, IPv6Address)):
6370
return "'%s'" % str(item)
@@ -69,19 +76,19 @@ def escape_param(item, context):
6976
return item
7077

7178

72-
def escape_params(params: Params, context: Dict) -> Params:
79+
def escape_params(params: Params, context: Dict, for_server=False) -> Params:
7380
"""Escape param to qualified string representation.
7481
7582
This function is not used in INSERT INTO queries.
7683
"""
7784
if isinstance(params, Dict):
7885
escaped = {
79-
key: escape_param(value, context)
86+
key: escape_param(value, context, for_server=for_server)
8087
for key, value in params.items()
8188
}
8289
else:
8390
escaped = tuple(
84-
escape_param(value, context)
91+
escape_param(value, context, for_server=for_server)
8592
for value in params
8693
)
8794

clickhouse_backend/models/fields/__init__.py

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -149,22 +149,6 @@ class DateField(FieldMixin, fields.DateField):
149149
"""Support integer or float may cause strange behavior,
150150
as integer and float are always treated as UTC timestamps,
151151
clickhouse store them as date in utc, which may not be what you want.
152-
153-
Due to a bug of clickhouse-driver 0.2.5,
154-
when set null and low_cardinality the same time to DateField or Date32Field,
155-
an exception will be raised when create a row.
156-
The same bug is also in UUIDField.
157-
Below is the minimum reproducing code:
158-
159-
from clickhouse_driver import Client
160-
from datetime import date
161-
162-
client = Client('localhost', settings={'allow_suspicious_low_cardinality_types': 1})
163-
client.execute('create table test (lnd LowCardinality(Nullable(Date))) engine MergeTree order by ()')
164-
client.execute('insert into test values', [{'lnd': date.today()}])
165-
166-
...
167-
AttributeError: 'int' object has no attribute 'year'
168152
"""
169153
def __init__(self, *args, low_cardinality=False, **kwargs):
170154
self.low_cardinality = low_cardinality

docs/Fields.md

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -165,13 +165,6 @@ DATABASES = {
165165
}
166166
```
167167

168-
**Note:**
169-
170-
Due to a [bug](https://github.com/mymarilyn/clickhouse-driver/issues/363) of clickhouse-driver 0.2.5,
171-
when set `null=True` and `low_cardinality=True` at the same time to UUIDField, an exception will be raised when inserting a row.
172-
173-
The same bug also exists in DateField and Date32Field.
174-
175168

176169
### Date and Date32
177170

@@ -206,14 +199,6 @@ clickhouse store them as date in the [server timezone](https://clickhouse.com/do
206199
So this feature is not implemented currently.
207200

208201

209-
**Note:**
210-
211-
Due to a [bug](https://github.com/mymarilyn/clickhouse-driver/issues/363) of clickhouse-driver 0.2.5,
212-
when set `null=True` and `low_cardinality=True` at the same time to DateField or Date32Field, an exception will be raised when inserting a row.
213-
214-
The same bug also exists in UUIDField.
215-
216-
217202
### DateTime and DateTime64
218203

219204
Fields importing path: `clickhouse_backend.models.DateTime[64]Field`

setup.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ def read(name):
2121
long_description_content_type="text/markdown",
2222
license="MIT",
2323
keywords='Django ClickHouse database backend engine driver',
24-
python_requires='>=3.6, <4',
24+
python_requires='>=3.7, <4',
2525
install_requires=[
2626
"django>=3.2",
27-
"clickhouse-driver==0.2.5",
27+
"clickhouse-driver==0.2.6",
2828
"clickhouse-pool==0.5.3",
2929
],
3030
classifiers=[
@@ -38,7 +38,6 @@ def read(name):
3838
"Programming Language :: Python",
3939
"Programming Language :: Python :: 3",
4040
"Programming Language :: Python :: 3 :: Only",
41-
"Programming Language :: Python :: 3.6",
4241
"Programming Language :: Python :: 3.7",
4342
"Programming Language :: Python :: 3.8",
4443
"Programming Language :: Python :: 3.9",

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[tox]
2-
env_list = py{36,37}-django32, py{38,39,310,311}-django{32,40,41}
2+
env_list = py37-django32, py{38,39,310,311}-django{32,40,41}
33

44
[testenv]
55
deps =

0 commit comments

Comments
 (0)