|
23 | 23 | import calendar |
24 | 24 | import heapq |
25 | 25 | import io |
26 | | -import json |
27 | 26 | import logging |
28 | 27 | import os |
29 | 28 | import re |
|
37 | 36 | from urllib.parse import urlparse |
38 | 37 | from uuid import UUID |
39 | 38 |
|
| 39 | +import orjson |
40 | 40 | import urllib3 |
41 | 41 | from urllib3 import connection_from_url |
42 | 42 | from urllib3.connection import HTTPConnection |
@@ -86,25 +86,31 @@ def super_len(o): |
86 | 86 | return None |
87 | 87 |
|
88 | 88 |
|
89 | | -class CrateJsonEncoder(json.JSONEncoder): |
90 | | - epoch_aware = datetime(1970, 1, 1, tzinfo=timezone.utc) |
91 | | - epoch_naive = datetime(1970, 1, 1) |
92 | | - |
93 | | - def default(self, o): |
94 | | - if isinstance(o, (Decimal, UUID)): |
95 | | - return str(o) |
96 | | - if isinstance(o, datetime): |
97 | | - if o.tzinfo is not None: |
98 | | - delta = o - self.epoch_aware |
99 | | - else: |
100 | | - delta = o - self.epoch_naive |
101 | | - return int( |
102 | | - delta.microseconds / 1000.0 |
103 | | - + (delta.seconds + delta.days * 24 * 3600) * 1000.0 |
104 | | - ) |
105 | | - if isinstance(o, date): |
106 | | - return calendar.timegm(o.timetuple()) * 1000 |
107 | | - return json.JSONEncoder.default(self, o) |
| 89 | +epoch_aware = datetime(1970, 1, 1, tzinfo=timezone.utc) |
| 90 | +epoch_naive = datetime(1970, 1, 1) |
| 91 | + |
| 92 | + |
| 93 | +def cratedb_json_encoder(obj): |
| 94 | + """ |
| 95 | + Encoder function for orjson. |
| 96 | +
|
| 97 | + https://github.com/ijl/orjson#default |
| 98 | + https://github.com/ijl/orjson#opt_passthrough_datetime |
| 99 | + """ |
| 100 | + if isinstance(obj, (Decimal, UUID)): |
| 101 | + return str(obj) |
| 102 | + if isinstance(obj, datetime): |
| 103 | + if obj.tzinfo is not None: |
| 104 | + delta = obj - epoch_aware |
| 105 | + else: |
| 106 | + delta = obj - epoch_naive |
| 107 | + return int( |
| 108 | + delta.microseconds / 1000.0 |
| 109 | + + (delta.seconds + delta.days * 24 * 3600) * 1000.0 |
| 110 | + ) |
| 111 | + if isinstance(obj, date): |
| 112 | + return calendar.timegm(obj.timetuple()) * 1000 |
| 113 | + return obj |
108 | 114 |
|
109 | 115 |
|
110 | 116 | class Server: |
@@ -180,7 +186,7 @@ def close(self): |
180 | 186 |
|
181 | 187 | def _json_from_response(response): |
182 | 188 | try: |
183 | | - return json.loads(response.data.decode("utf-8")) |
| 189 | + return orjson.loads(response.data) |
184 | 190 | except ValueError as ex: |
185 | 191 | raise ProgrammingError( |
186 | 192 | "Invalid server response of content-type '{}':\n{}".format( |
@@ -223,7 +229,7 @@ def _raise_for_status_real(response): |
223 | 229 | if response.status == 503: |
224 | 230 | raise ConnectionError(message) |
225 | 231 | if response.headers.get("content-type", "").startswith("application/json"): |
226 | | - data = json.loads(response.data.decode("utf-8")) |
| 232 | + data = orjson.loads(response.data) |
227 | 233 | error = data.get("error", {}) |
228 | 234 | error_trace = data.get("error_trace", None) |
229 | 235 | if "results" in data: |
@@ -334,7 +340,11 @@ def _create_sql_payload(stmt, args, bulk_args): |
334 | 340 | data["args"] = args |
335 | 341 | if bulk_args: |
336 | 342 | data["bulk_args"] = bulk_args |
337 | | - return json.dumps(data, cls=CrateJsonEncoder) |
| 343 | + return orjson.dumps( |
| 344 | + data, |
| 345 | + default=cratedb_json_encoder, |
| 346 | + option=orjson.OPT_PASSTHROUGH_DATETIME, |
| 347 | + ) |
338 | 348 |
|
339 | 349 |
|
340 | 350 | def _get_socket_opts( |
|
0 commit comments