clickhouse-driver 0.2.1__cp39-cp39-win_amd64.whl → 0.2.8__cp39-cp39-win_amd64.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.
- clickhouse_driver/__init__.py +9 -9
- clickhouse_driver/block.py +227 -195
- clickhouse_driver/blockstreamprofileinfo.py +22 -22
- clickhouse_driver/bufferedreader.cp39-win_amd64.pyd +0 -0
- clickhouse_driver/bufferedwriter.cp39-win_amd64.pyd +0 -0
- clickhouse_driver/client.py +896 -666
- clickhouse_driver/clientinfo.py +119 -80
- clickhouse_driver/columns/arraycolumn.py +161 -150
- clickhouse_driver/columns/base.py +221 -147
- clickhouse_driver/columns/boolcolumn.py +7 -0
- clickhouse_driver/columns/datecolumn.py +108 -49
- clickhouse_driver/columns/datetimecolumn.py +202 -207
- clickhouse_driver/columns/decimalcolumn.py +116 -118
- clickhouse_driver/columns/enumcolumn.py +119 -119
- clickhouse_driver/columns/exceptions.py +12 -12
- clickhouse_driver/columns/floatcolumn.py +34 -34
- clickhouse_driver/columns/intcolumn.py +157 -157
- clickhouse_driver/columns/intervalcolumn.py +33 -33
- clickhouse_driver/columns/ipcolumn.py +118 -118
- clickhouse_driver/columns/jsoncolumn.py +37 -0
- clickhouse_driver/columns/largeint.cp39-win_amd64.pyd +0 -0
- clickhouse_driver/columns/lowcardinalitycolumn.py +142 -123
- clickhouse_driver/columns/mapcolumn.py +73 -58
- clickhouse_driver/columns/nestedcolumn.py +10 -0
- clickhouse_driver/columns/nothingcolumn.py +13 -13
- clickhouse_driver/columns/nullablecolumn.py +7 -7
- clickhouse_driver/columns/nullcolumn.py +15 -15
- clickhouse_driver/columns/numpy/base.py +47 -14
- clickhouse_driver/columns/numpy/boolcolumn.py +8 -0
- clickhouse_driver/columns/numpy/datecolumn.py +19 -12
- clickhouse_driver/columns/numpy/datetimecolumn.py +143 -145
- clickhouse_driver/columns/numpy/floatcolumn.py +24 -13
- clickhouse_driver/columns/numpy/intcolumn.py +43 -43
- clickhouse_driver/columns/numpy/lowcardinalitycolumn.py +96 -83
- clickhouse_driver/columns/numpy/service.py +58 -80
- clickhouse_driver/columns/numpy/stringcolumn.py +78 -76
- clickhouse_driver/columns/numpy/tuplecolumn.py +37 -0
- clickhouse_driver/columns/service.py +185 -131
- clickhouse_driver/columns/simpleaggregatefunctioncolumn.py +7 -7
- clickhouse_driver/columns/stringcolumn.py +73 -73
- clickhouse_driver/columns/tuplecolumn.py +63 -65
- clickhouse_driver/columns/util.py +60 -0
- clickhouse_driver/columns/uuidcolumn.py +64 -64
- clickhouse_driver/compression/__init__.py +28 -28
- clickhouse_driver/compression/base.py +87 -52
- clickhouse_driver/compression/lz4.py +21 -55
- clickhouse_driver/compression/lz4hc.py +9 -9
- clickhouse_driver/compression/zstd.py +20 -51
- clickhouse_driver/connection.py +784 -632
- clickhouse_driver/context.py +36 -36
- clickhouse_driver/dbapi/__init__.py +62 -62
- clickhouse_driver/dbapi/connection.py +99 -96
- clickhouse_driver/dbapi/cursor.py +370 -368
- clickhouse_driver/dbapi/errors.py +40 -40
- clickhouse_driver/dbapi/extras.py +73 -0
- clickhouse_driver/defines.py +55 -42
- clickhouse_driver/errors.py +453 -446
- clickhouse_driver/log.py +48 -44
- clickhouse_driver/numpy/block.py +8 -8
- clickhouse_driver/numpy/helpers.py +25 -25
- clickhouse_driver/numpy/result.py +123 -123
- clickhouse_driver/opentelemetry.py +43 -0
- clickhouse_driver/progress.py +38 -32
- clickhouse_driver/protocol.py +114 -105
- clickhouse_driver/queryprocessingstage.py +8 -8
- clickhouse_driver/reader.py +69 -69
- clickhouse_driver/readhelpers.py +26 -26
- clickhouse_driver/result.py +144 -144
- clickhouse_driver/settings/available.py +405 -405
- clickhouse_driver/settings/types.py +50 -50
- clickhouse_driver/settings/writer.py +34 -29
- clickhouse_driver/streams/compressed.py +88 -88
- clickhouse_driver/streams/native.py +102 -90
- clickhouse_driver/util/compat.py +39 -0
- clickhouse_driver/util/escape.py +94 -55
- clickhouse_driver/util/helpers.py +57 -57
- clickhouse_driver/varint.cp39-win_amd64.pyd +0 -0
- clickhouse_driver/writer.py +67 -67
- {clickhouse_driver-0.2.1.dist-info → clickhouse_driver-0.2.8.dist-info}/LICENSE +21 -21
- clickhouse_driver-0.2.8.dist-info/METADATA +201 -0
- clickhouse_driver-0.2.8.dist-info/RECORD +89 -0
- {clickhouse_driver-0.2.1.dist-info → clickhouse_driver-0.2.8.dist-info}/WHEEL +1 -1
- clickhouse_driver-0.2.1.dist-info/METADATA +0 -24
- clickhouse_driver-0.2.1.dist-info/RECORD +0 -80
- {clickhouse_driver-0.2.1.dist-info → clickhouse_driver-0.2.8.dist-info}/top_level.txt +0 -0
|
@@ -1,207 +1,202 @@
|
|
|
1
|
-
from datetime import datetime
|
|
2
|
-
|
|
3
|
-
from pytz import timezone as get_timezone, utc
|
|
4
|
-
from
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
self.
|
|
18
|
-
self.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
#
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
self.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
#
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
#
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
int(
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if tz_name:
|
|
205
|
-
timezone = get_timezone(tz_name)
|
|
206
|
-
|
|
207
|
-
return cls(timezone=timezone, offset_naive=offset_naive, **column_options)
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
|
|
3
|
+
from pytz import timezone as get_timezone, utc
|
|
4
|
+
from ..util.compat import get_localzone_name_compat
|
|
5
|
+
from .base import FormatColumn
|
|
6
|
+
|
|
7
|
+
EPOCH = datetime(1970, 1, 1, tzinfo=utc)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DateTimeColumn(FormatColumn):
|
|
11
|
+
ch_type = 'DateTime'
|
|
12
|
+
py_types = (datetime, int)
|
|
13
|
+
format = 'I'
|
|
14
|
+
|
|
15
|
+
def __init__(self, timezone=None, offset_naive=True, **kwargs):
|
|
16
|
+
self.timezone = timezone
|
|
17
|
+
self.offset_naive = offset_naive
|
|
18
|
+
super(DateTimeColumn, self).__init__(**kwargs)
|
|
19
|
+
|
|
20
|
+
def after_read_items(self, items, nulls_map=None):
|
|
21
|
+
tz = self.timezone
|
|
22
|
+
fromts = datetime.fromtimestamp
|
|
23
|
+
|
|
24
|
+
# A bit ugly copy-paste. But it helps save time on items
|
|
25
|
+
# processing by avoiding lambda calls or if in loop.
|
|
26
|
+
if self.offset_naive:
|
|
27
|
+
if tz:
|
|
28
|
+
if nulls_map is None:
|
|
29
|
+
return tuple(
|
|
30
|
+
fromts(item, tz).replace(tzinfo=None)
|
|
31
|
+
for item in items
|
|
32
|
+
)
|
|
33
|
+
else:
|
|
34
|
+
return tuple(
|
|
35
|
+
(None if is_null else
|
|
36
|
+
fromts(items[i], tz).replace(tzinfo=None))
|
|
37
|
+
for i, is_null in enumerate(nulls_map)
|
|
38
|
+
)
|
|
39
|
+
else:
|
|
40
|
+
if nulls_map is None:
|
|
41
|
+
return tuple(fromts(item) for item in items)
|
|
42
|
+
else:
|
|
43
|
+
return tuple(
|
|
44
|
+
(None if is_null else fromts(items[i]))
|
|
45
|
+
for i, is_null in enumerate(nulls_map)
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
else:
|
|
49
|
+
if nulls_map is None:
|
|
50
|
+
return tuple(fromts(item, tz) for item in items)
|
|
51
|
+
else:
|
|
52
|
+
return tuple(
|
|
53
|
+
(None if is_null else fromts(items[i], tz))
|
|
54
|
+
for i, is_null in enumerate(nulls_map)
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def before_write_items(self, items, nulls_map=None):
|
|
58
|
+
timezone = self.timezone
|
|
59
|
+
null_value = self.null_value
|
|
60
|
+
to_timestamp = datetime.timestamp
|
|
61
|
+
|
|
62
|
+
for i, item in enumerate(items):
|
|
63
|
+
if nulls_map and nulls_map[i]:
|
|
64
|
+
items[i] = null_value
|
|
65
|
+
continue
|
|
66
|
+
|
|
67
|
+
if isinstance(item, int):
|
|
68
|
+
# support supplying raw integers to avoid
|
|
69
|
+
# costly timezone conversions when using datetime
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
if timezone:
|
|
73
|
+
# Set server's timezone for offset-naive datetime.
|
|
74
|
+
if item.tzinfo is None:
|
|
75
|
+
item = timezone.localize(item)
|
|
76
|
+
|
|
77
|
+
item = item.astimezone(utc)
|
|
78
|
+
|
|
79
|
+
else:
|
|
80
|
+
# If datetime is offset-aware use it's timezone.
|
|
81
|
+
if item.tzinfo is not None:
|
|
82
|
+
item = item.astimezone(utc)
|
|
83
|
+
|
|
84
|
+
items[i] = int(to_timestamp(item))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class DateTime64Column(DateTimeColumn):
|
|
88
|
+
ch_type = 'DateTime64'
|
|
89
|
+
format = 'q'
|
|
90
|
+
|
|
91
|
+
max_scale = 6
|
|
92
|
+
|
|
93
|
+
def __init__(self, scale=0, **kwargs):
|
|
94
|
+
self.scale = scale
|
|
95
|
+
super(DateTime64Column, self).__init__(**kwargs)
|
|
96
|
+
|
|
97
|
+
def after_read_items(self, items, nulls_map=None):
|
|
98
|
+
scale = float(10 ** self.scale)
|
|
99
|
+
|
|
100
|
+
tz = self.timezone
|
|
101
|
+
fromts = datetime.fromtimestamp
|
|
102
|
+
|
|
103
|
+
# A bit ugly copy-paste. But it helps save time on items
|
|
104
|
+
# processing by avoiding lambda calls or if in loop.
|
|
105
|
+
if self.offset_naive:
|
|
106
|
+
if tz:
|
|
107
|
+
if nulls_map is None:
|
|
108
|
+
return tuple(
|
|
109
|
+
fromts(item / scale, tz).replace(tzinfo=None)
|
|
110
|
+
for item in items
|
|
111
|
+
)
|
|
112
|
+
else:
|
|
113
|
+
return tuple(
|
|
114
|
+
(None if is_null else
|
|
115
|
+
fromts(items[i] / scale, tz).replace(tzinfo=None))
|
|
116
|
+
for i, is_null in enumerate(nulls_map)
|
|
117
|
+
)
|
|
118
|
+
else:
|
|
119
|
+
if nulls_map is None:
|
|
120
|
+
return tuple(fromts(item / scale) for item in items)
|
|
121
|
+
else:
|
|
122
|
+
return tuple(
|
|
123
|
+
(None if is_null else fromts(items[i] / scale))
|
|
124
|
+
for i, is_null in enumerate(nulls_map)
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
else:
|
|
128
|
+
if nulls_map is None:
|
|
129
|
+
return tuple(fromts(item / scale, tz) for item in items)
|
|
130
|
+
else:
|
|
131
|
+
return tuple(
|
|
132
|
+
(None if is_null else fromts(items[i] / scale, tz))
|
|
133
|
+
for i, is_null in enumerate(nulls_map)
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
def before_write_items(self, items, nulls_map=None):
|
|
137
|
+
scale = 10 ** self.scale
|
|
138
|
+
frac_scale = 10 ** (self.max_scale - self.scale)
|
|
139
|
+
|
|
140
|
+
timezone = self.timezone
|
|
141
|
+
null_value = self.null_value
|
|
142
|
+
to_timestamp = datetime.timestamp
|
|
143
|
+
|
|
144
|
+
for i, item in enumerate(items):
|
|
145
|
+
if nulls_map and nulls_map[i]:
|
|
146
|
+
items[i] = null_value
|
|
147
|
+
continue
|
|
148
|
+
|
|
149
|
+
if isinstance(item, int):
|
|
150
|
+
# support supplying raw integers to avoid
|
|
151
|
+
# costly timezone conversions when using datetime
|
|
152
|
+
continue
|
|
153
|
+
|
|
154
|
+
if timezone:
|
|
155
|
+
# Set server's timezone for offset-naive datetime.
|
|
156
|
+
if item.tzinfo is None:
|
|
157
|
+
item = timezone.localize(item)
|
|
158
|
+
|
|
159
|
+
item = item.astimezone(utc)
|
|
160
|
+
|
|
161
|
+
else:
|
|
162
|
+
# If datetime is offset-aware use it's timezone.
|
|
163
|
+
if item.tzinfo is not None:
|
|
164
|
+
item = item.astimezone(utc)
|
|
165
|
+
|
|
166
|
+
items[i] = (
|
|
167
|
+
int(to_timestamp(item)) * scale +
|
|
168
|
+
int(item.microsecond / frac_scale)
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def create_datetime_column(spec, column_options):
|
|
173
|
+
if spec.startswith('DateTime64'):
|
|
174
|
+
cls = DateTime64Column
|
|
175
|
+
spec = spec[11:-1]
|
|
176
|
+
params = spec.split(',', 1)
|
|
177
|
+
column_options['scale'] = int(params[0])
|
|
178
|
+
if len(params) > 1:
|
|
179
|
+
spec = params[1].strip() + ')'
|
|
180
|
+
else:
|
|
181
|
+
cls = DateTimeColumn
|
|
182
|
+
spec = spec[9:]
|
|
183
|
+
|
|
184
|
+
context = column_options['context']
|
|
185
|
+
|
|
186
|
+
tz_name = timezone = None
|
|
187
|
+
offset_naive = True
|
|
188
|
+
|
|
189
|
+
# Use column's timezone if it's specified.
|
|
190
|
+
if spec and spec[-1] == ')':
|
|
191
|
+
tz_name = spec[1:-2]
|
|
192
|
+
offset_naive = False
|
|
193
|
+
else:
|
|
194
|
+
if not context.settings.get('use_client_time_zone', False):
|
|
195
|
+
local_timezone = get_localzone_name_compat()
|
|
196
|
+
if local_timezone != context.server_info.timezone:
|
|
197
|
+
tz_name = context.server_info.timezone
|
|
198
|
+
|
|
199
|
+
if tz_name:
|
|
200
|
+
timezone = get_timezone(tz_name)
|
|
201
|
+
|
|
202
|
+
return cls(timezone=timezone, offset_naive=offset_naive, **column_options)
|
|
@@ -1,118 +1,116 @@
|
|
|
1
|
-
from decimal import Decimal, localcontext
|
|
2
|
-
|
|
3
|
-
from .base import FormatColumn
|
|
4
|
-
from .exceptions import ColumnTypeMismatchException
|
|
5
|
-
from .intcolumn import Int128Column, Int256Column
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class DecimalColumn(FormatColumn):
|
|
9
|
-
py_types = (Decimal, float, int)
|
|
10
|
-
max_precision = None
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
self.
|
|
15
|
-
self.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if
|
|
23
|
-
raise ColumnTypeMismatchException(value)
|
|
24
|
-
|
|
25
|
-
self.check_item = check_item
|
|
26
|
-
|
|
27
|
-
def after_read_items(self, items, nulls_map=None):
|
|
28
|
-
if self.scale >= 1:
|
|
29
|
-
scale = 10 ** self.scale
|
|
30
|
-
|
|
31
|
-
if nulls_map is None:
|
|
32
|
-
return tuple(Decimal(item) / scale for item in items)
|
|
33
|
-
else:
|
|
34
|
-
return tuple(
|
|
35
|
-
(None if is_null else Decimal(items[i]) / scale)
|
|
36
|
-
for i, is_null in enumerate(nulls_map)
|
|
37
|
-
)
|
|
38
|
-
else:
|
|
39
|
-
if nulls_map is None:
|
|
40
|
-
return tuple(Decimal(item) for item in items)
|
|
41
|
-
else:
|
|
42
|
-
return tuple(
|
|
43
|
-
(None if is_null else Decimal(items[i]))
|
|
44
|
-
for i, is_null in enumerate(nulls_map)
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
def before_write_items(self, items, nulls_map=None):
|
|
48
|
-
null_value = self.null_value
|
|
49
|
-
|
|
50
|
-
if self.scale >= 1:
|
|
51
|
-
scale = 10 ** self.scale
|
|
52
|
-
|
|
53
|
-
for i, item in enumerate(items):
|
|
54
|
-
if nulls_map and nulls_map[i]:
|
|
55
|
-
items[i] = null_value
|
|
56
|
-
else:
|
|
57
|
-
items[i] = int(Decimal(str(item)) * scale)
|
|
58
|
-
|
|
59
|
-
else:
|
|
60
|
-
for i, item in enumerate(items):
|
|
61
|
-
if nulls_map and nulls_map[i]:
|
|
62
|
-
items[i] = null_value
|
|
63
|
-
else:
|
|
64
|
-
items[i] = int(Decimal(str(item)))
|
|
65
|
-
|
|
66
|
-
# Override default precision to the maximum supported by underlying type.
|
|
67
|
-
def _write_data(self, items, buf):
|
|
68
|
-
with localcontext() as ctx:
|
|
69
|
-
ctx.prec = self.max_precision
|
|
70
|
-
super(DecimalColumn, self)._write_data(items, buf)
|
|
71
|
-
|
|
72
|
-
def _read_data(self, n_items, buf, nulls_map=None):
|
|
73
|
-
with localcontext() as ctx:
|
|
74
|
-
ctx.prec = self.max_precision
|
|
75
|
-
return super(DecimalColumn, self)._read_data(
|
|
76
|
-
n_items, buf, nulls_map=nulls_map
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
class Decimal32Column(DecimalColumn):
|
|
81
|
-
format = 'i'
|
|
82
|
-
max_precision = 9
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
#
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
cls =
|
|
111
|
-
elif precision <=
|
|
112
|
-
cls =
|
|
113
|
-
|
|
114
|
-
cls =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
return cls(precision, scale, **column_options)
|
|
1
|
+
from decimal import Decimal, localcontext
|
|
2
|
+
|
|
3
|
+
from .base import FormatColumn
|
|
4
|
+
from .exceptions import ColumnTypeMismatchException
|
|
5
|
+
from .intcolumn import Int128Column, Int256Column
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DecimalColumn(FormatColumn):
|
|
9
|
+
py_types = (Decimal, float, int)
|
|
10
|
+
max_precision = None
|
|
11
|
+
|
|
12
|
+
def __init__(self, precision, scale, types_check=False, **kwargs):
|
|
13
|
+
self.precision = precision
|
|
14
|
+
self.scale = scale
|
|
15
|
+
super(DecimalColumn, self).__init__(**kwargs)
|
|
16
|
+
|
|
17
|
+
if types_check:
|
|
18
|
+
def check_item(value):
|
|
19
|
+
parts = str(value).split('.')
|
|
20
|
+
int_part = parts[0]
|
|
21
|
+
|
|
22
|
+
if len(int_part) > precision:
|
|
23
|
+
raise ColumnTypeMismatchException(value)
|
|
24
|
+
|
|
25
|
+
self.check_item = check_item
|
|
26
|
+
|
|
27
|
+
def after_read_items(self, items, nulls_map=None):
|
|
28
|
+
if self.scale >= 1:
|
|
29
|
+
scale = 10 ** self.scale
|
|
30
|
+
|
|
31
|
+
if nulls_map is None:
|
|
32
|
+
return tuple(Decimal(item) / scale for item in items)
|
|
33
|
+
else:
|
|
34
|
+
return tuple(
|
|
35
|
+
(None if is_null else Decimal(items[i]) / scale)
|
|
36
|
+
for i, is_null in enumerate(nulls_map)
|
|
37
|
+
)
|
|
38
|
+
else:
|
|
39
|
+
if nulls_map is None:
|
|
40
|
+
return tuple(Decimal(item) for item in items)
|
|
41
|
+
else:
|
|
42
|
+
return tuple(
|
|
43
|
+
(None if is_null else Decimal(items[i]))
|
|
44
|
+
for i, is_null in enumerate(nulls_map)
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
def before_write_items(self, items, nulls_map=None):
|
|
48
|
+
null_value = self.null_value
|
|
49
|
+
|
|
50
|
+
if self.scale >= 1:
|
|
51
|
+
scale = 10 ** self.scale
|
|
52
|
+
|
|
53
|
+
for i, item in enumerate(items):
|
|
54
|
+
if nulls_map and nulls_map[i]:
|
|
55
|
+
items[i] = null_value
|
|
56
|
+
else:
|
|
57
|
+
items[i] = int(Decimal(str(item)) * scale)
|
|
58
|
+
|
|
59
|
+
else:
|
|
60
|
+
for i, item in enumerate(items):
|
|
61
|
+
if nulls_map and nulls_map[i]:
|
|
62
|
+
items[i] = null_value
|
|
63
|
+
else:
|
|
64
|
+
items[i] = int(Decimal(str(item)))
|
|
65
|
+
|
|
66
|
+
# Override default precision to the maximum supported by underlying type.
|
|
67
|
+
def _write_data(self, items, buf):
|
|
68
|
+
with localcontext() as ctx:
|
|
69
|
+
ctx.prec = self.max_precision
|
|
70
|
+
super(DecimalColumn, self)._write_data(items, buf)
|
|
71
|
+
|
|
72
|
+
def _read_data(self, n_items, buf, nulls_map=None):
|
|
73
|
+
with localcontext() as ctx:
|
|
74
|
+
ctx.prec = self.max_precision
|
|
75
|
+
return super(DecimalColumn, self)._read_data(
|
|
76
|
+
n_items, buf, nulls_map=nulls_map
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class Decimal32Column(DecimalColumn):
|
|
81
|
+
format = 'i'
|
|
82
|
+
max_precision = 9
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class Decimal64Column(DecimalColumn):
|
|
86
|
+
format = 'q'
|
|
87
|
+
max_precision = 18
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class Decimal128Column(DecimalColumn, Int128Column):
|
|
91
|
+
max_precision = 38
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class Decimal256Column(DecimalColumn, Int256Column):
|
|
95
|
+
max_precision = 76
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def create_decimal_column(spec, column_options):
|
|
99
|
+
precision, scale = spec[8:-1].split(',')
|
|
100
|
+
precision, scale = int(precision), int(scale)
|
|
101
|
+
|
|
102
|
+
# Maximum precisions for underlying types are:
|
|
103
|
+
# Int32 10**9
|
|
104
|
+
# Int64 10**18
|
|
105
|
+
# Int128 10**38
|
|
106
|
+
# Int256 10**76
|
|
107
|
+
if precision <= 9:
|
|
108
|
+
cls = Decimal32Column
|
|
109
|
+
elif precision <= 18:
|
|
110
|
+
cls = Decimal64Column
|
|
111
|
+
elif precision <= 38:
|
|
112
|
+
cls = Decimal128Column
|
|
113
|
+
else:
|
|
114
|
+
cls = Decimal256Column
|
|
115
|
+
|
|
116
|
+
return cls(precision, scale, **column_options)
|