singlestoredb 0.3.3__py3-none-any.whl → 1.0.3__py3-none-any.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 singlestoredb might be problematic. Click here for more details.
- singlestoredb/__init__.py +33 -2
- singlestoredb/alchemy/__init__.py +90 -0
- singlestoredb/auth.py +6 -4
- singlestoredb/config.py +116 -16
- singlestoredb/connection.py +489 -523
- singlestoredb/converters.py +275 -26
- singlestoredb/exceptions.py +30 -4
- singlestoredb/functions/__init__.py +1 -0
- singlestoredb/functions/decorator.py +142 -0
- singlestoredb/functions/dtypes.py +1639 -0
- singlestoredb/functions/ext/__init__.py +2 -0
- singlestoredb/functions/ext/arrow.py +375 -0
- singlestoredb/functions/ext/asgi.py +661 -0
- singlestoredb/functions/ext/json.py +427 -0
- singlestoredb/functions/ext/mmap.py +306 -0
- singlestoredb/functions/ext/rowdat_1.py +744 -0
- singlestoredb/functions/signature.py +673 -0
- singlestoredb/fusion/__init__.py +11 -0
- singlestoredb/fusion/graphql.py +213 -0
- singlestoredb/fusion/handler.py +621 -0
- singlestoredb/fusion/handlers/__init__.py +0 -0
- singlestoredb/fusion/handlers/stage.py +257 -0
- singlestoredb/fusion/handlers/utils.py +162 -0
- singlestoredb/fusion/handlers/workspace.py +412 -0
- singlestoredb/fusion/registry.py +164 -0
- singlestoredb/fusion/result.py +399 -0
- singlestoredb/http/__init__.py +27 -0
- singlestoredb/http/connection.py +1192 -0
- singlestoredb/management/__init__.py +3 -2
- singlestoredb/management/billing_usage.py +148 -0
- singlestoredb/management/cluster.py +19 -14
- singlestoredb/management/manager.py +100 -40
- singlestoredb/management/organization.py +188 -0
- singlestoredb/management/region.py +6 -8
- singlestoredb/management/utils.py +253 -4
- singlestoredb/management/workspace.py +1153 -35
- singlestoredb/mysql/__init__.py +177 -0
- singlestoredb/mysql/_auth.py +298 -0
- singlestoredb/mysql/charset.py +214 -0
- singlestoredb/mysql/connection.py +1814 -0
- singlestoredb/mysql/constants/CLIENT.py +38 -0
- singlestoredb/mysql/constants/COMMAND.py +32 -0
- singlestoredb/mysql/constants/CR.py +78 -0
- singlestoredb/mysql/constants/ER.py +474 -0
- singlestoredb/mysql/constants/FIELD_TYPE.py +32 -0
- singlestoredb/mysql/constants/FLAG.py +15 -0
- singlestoredb/mysql/constants/SERVER_STATUS.py +10 -0
- singlestoredb/mysql/constants/__init__.py +0 -0
- singlestoredb/mysql/converters.py +271 -0
- singlestoredb/mysql/cursors.py +713 -0
- singlestoredb/mysql/err.py +92 -0
- singlestoredb/mysql/optionfile.py +20 -0
- singlestoredb/mysql/protocol.py +388 -0
- singlestoredb/mysql/tests/__init__.py +19 -0
- singlestoredb/mysql/tests/base.py +126 -0
- singlestoredb/mysql/tests/conftest.py +37 -0
- singlestoredb/mysql/tests/test_DictCursor.py +132 -0
- singlestoredb/mysql/tests/test_SSCursor.py +141 -0
- singlestoredb/mysql/tests/test_basic.py +452 -0
- singlestoredb/mysql/tests/test_connection.py +851 -0
- singlestoredb/mysql/tests/test_converters.py +58 -0
- singlestoredb/mysql/tests/test_cursor.py +141 -0
- singlestoredb/mysql/tests/test_err.py +16 -0
- singlestoredb/mysql/tests/test_issues.py +514 -0
- singlestoredb/mysql/tests/test_load_local.py +75 -0
- singlestoredb/mysql/tests/test_nextset.py +88 -0
- singlestoredb/mysql/tests/test_optionfile.py +27 -0
- singlestoredb/mysql/tests/thirdparty/__init__.py +6 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/__init__.py +9 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/capabilities.py +323 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/dbapi20.py +865 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_capabilities.py +110 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_dbapi20.py +224 -0
- singlestoredb/mysql/tests/thirdparty/test_MySQLdb/test_MySQLdb_nonstandard.py +101 -0
- singlestoredb/mysql/times.py +23 -0
- singlestoredb/pytest.py +283 -0
- singlestoredb/tests/empty.sql +0 -0
- singlestoredb/tests/ext_funcs/__init__.py +385 -0
- singlestoredb/tests/test.sql +210 -0
- singlestoredb/tests/test2.sql +1 -0
- singlestoredb/tests/test_basics.py +482 -117
- singlestoredb/tests/test_config.py +13 -15
- singlestoredb/tests/test_connection.py +241 -289
- singlestoredb/tests/test_dbapi.py +27 -0
- singlestoredb/tests/test_exceptions.py +0 -2
- singlestoredb/tests/test_ext_func.py +1193 -0
- singlestoredb/tests/test_ext_func_data.py +1101 -0
- singlestoredb/tests/test_fusion.py +465 -0
- singlestoredb/tests/test_http.py +32 -28
- singlestoredb/tests/test_management.py +588 -10
- singlestoredb/tests/test_plugin.py +33 -0
- singlestoredb/tests/test_results.py +11 -14
- singlestoredb/tests/test_types.py +0 -2
- singlestoredb/tests/test_udf.py +687 -0
- singlestoredb/tests/test_xdict.py +0 -2
- singlestoredb/tests/utils.py +3 -4
- singlestoredb/types.py +4 -5
- singlestoredb/utils/config.py +71 -12
- singlestoredb/utils/convert_rows.py +0 -2
- singlestoredb/utils/debug.py +13 -0
- singlestoredb/utils/mogrify.py +151 -0
- singlestoredb/utils/results.py +4 -3
- singlestoredb/utils/xdict.py +12 -12
- singlestoredb-1.0.3.dist-info/METADATA +139 -0
- singlestoredb-1.0.3.dist-info/RECORD +112 -0
- {singlestoredb-0.3.3.dist-info → singlestoredb-1.0.3.dist-info}/WHEEL +1 -1
- singlestoredb-1.0.3.dist-info/entry_points.txt +2 -0
- singlestoredb/drivers/__init__.py +0 -46
- singlestoredb/drivers/base.py +0 -200
- singlestoredb/drivers/cymysql.py +0 -40
- singlestoredb/drivers/http.py +0 -49
- singlestoredb/drivers/mariadb.py +0 -42
- singlestoredb/drivers/mysqlconnector.py +0 -51
- singlestoredb/drivers/mysqldb.py +0 -62
- singlestoredb/drivers/pymysql.py +0 -39
- singlestoredb/drivers/pyodbc.py +0 -67
- singlestoredb/http.py +0 -794
- singlestoredb-0.3.3.dist-info/METADATA +0 -105
- singlestoredb-0.3.3.dist-info/RECORD +0 -46
- {singlestoredb-0.3.3.dist-info → singlestoredb-1.0.3.dist-info}/LICENSE +0 -0
- {singlestoredb-0.3.3.dist-info → singlestoredb-1.0.3.dist-info}/top_level.txt +0 -0
|
@@ -1,13 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env python
|
|
2
2
|
# type: ignore
|
|
3
3
|
"""Basic SingleStoreDB connection testing."""
|
|
4
|
-
from __future__ import annotations
|
|
5
|
-
|
|
6
4
|
import datetime
|
|
7
5
|
import decimal
|
|
6
|
+
import math
|
|
8
7
|
import os
|
|
9
8
|
import unittest
|
|
10
9
|
|
|
10
|
+
from requests.exceptions import InvalidJSONError
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
import numpy as np
|
|
14
|
+
has_numpy = True
|
|
15
|
+
except ImportError:
|
|
16
|
+
has_numpy = False
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
import shapely.wkt
|
|
20
|
+
has_shapely = True
|
|
21
|
+
except ImportError:
|
|
22
|
+
has_shapely = False
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
import pygeos
|
|
26
|
+
from pygeos.testing import assert_geometries_equal
|
|
27
|
+
has_pygeos = True
|
|
28
|
+
except ImportError:
|
|
29
|
+
has_pygeos = False
|
|
30
|
+
|
|
11
31
|
import singlestoredb as s2
|
|
12
32
|
from . import utils
|
|
13
33
|
# import traceback
|
|
@@ -31,7 +51,6 @@ class TestBasics(unittest.TestCase):
|
|
|
31
51
|
def setUp(self):
|
|
32
52
|
self.conn = s2.connect(database=type(self).dbname)
|
|
33
53
|
self.cur = self.conn.cursor()
|
|
34
|
-
self.driver = self.conn._driver.dbapi.__name__
|
|
35
54
|
|
|
36
55
|
def tearDown(self):
|
|
37
56
|
try:
|
|
@@ -71,7 +90,7 @@ class TestBasics(unittest.TestCase):
|
|
|
71
90
|
('e', 'elephants', 0),
|
|
72
91
|
]), out
|
|
73
92
|
|
|
74
|
-
assert rowcount
|
|
93
|
+
assert rowcount in (5, -1), rowcount
|
|
75
94
|
assert rownumber == 5, rownumber
|
|
76
95
|
assert lastrowid is None, lastrowid
|
|
77
96
|
assert len(desc) == 3, desc
|
|
@@ -106,7 +125,7 @@ class TestBasics(unittest.TestCase):
|
|
|
106
125
|
('e', 'elephants', 0),
|
|
107
126
|
]), out
|
|
108
127
|
|
|
109
|
-
assert rowcount
|
|
128
|
+
assert rowcount in (5, -1), rowcount
|
|
110
129
|
assert rownumber == 5, rownumber
|
|
111
130
|
assert lastrowid is None, lastrowid
|
|
112
131
|
assert len(desc) == 3, desc
|
|
@@ -142,7 +161,7 @@ class TestBasics(unittest.TestCase):
|
|
|
142
161
|
('e', 'elephants', 0),
|
|
143
162
|
]), out
|
|
144
163
|
|
|
145
|
-
assert rowcount
|
|
164
|
+
assert rowcount in (5, -1), rowcount
|
|
146
165
|
assert rownumber == 5, rownumber
|
|
147
166
|
assert lastrowid is None, lastrowid
|
|
148
167
|
assert len(desc) == 3, desc
|
|
@@ -179,7 +198,7 @@ class TestBasics(unittest.TestCase):
|
|
|
179
198
|
assert self.cur.rownumber == 5, self.cur.rownumber
|
|
180
199
|
|
|
181
200
|
def test_execute_with_dict_params(self):
|
|
182
|
-
self.cur.execute('select * from data where id <
|
|
201
|
+
self.cur.execute('select * from data where id < %(name)s', dict(name='d'))
|
|
183
202
|
out = self.cur.fetchall()
|
|
184
203
|
|
|
185
204
|
desc = self.cur.description
|
|
@@ -192,7 +211,7 @@ class TestBasics(unittest.TestCase):
|
|
|
192
211
|
('c', 'cats', 5),
|
|
193
212
|
]), out
|
|
194
213
|
|
|
195
|
-
assert rowcount
|
|
214
|
+
assert rowcount in (3, -1), rowcount
|
|
196
215
|
assert lastrowid is None, lastrowid
|
|
197
216
|
assert len(desc) == 3, desc
|
|
198
217
|
assert desc[0].name == 'id', desc[0].name
|
|
@@ -202,8 +221,11 @@ class TestBasics(unittest.TestCase):
|
|
|
202
221
|
assert desc[2].name == 'value', desc[2].name
|
|
203
222
|
assert desc[2].type_code == 8, desc[2].type_code
|
|
204
223
|
|
|
224
|
+
with self.assertRaises(KeyError):
|
|
225
|
+
self.cur.execute('select * from data where id < %(name)s', dict(foo='d'))
|
|
226
|
+
|
|
205
227
|
def test_execute_with_positional_params(self):
|
|
206
|
-
self.cur.execute('select * from data where id <
|
|
228
|
+
self.cur.execute('select * from data where id < %s', ['d'])
|
|
207
229
|
out = self.cur.fetchall()
|
|
208
230
|
|
|
209
231
|
desc = self.cur.description
|
|
@@ -216,7 +238,7 @@ class TestBasics(unittest.TestCase):
|
|
|
216
238
|
('c', 'cats', 5),
|
|
217
239
|
]), out
|
|
218
240
|
|
|
219
|
-
assert rowcount
|
|
241
|
+
assert rowcount in (3, -1), rowcount
|
|
220
242
|
assert lastrowid is None, lastrowid
|
|
221
243
|
assert len(desc) == 3, desc
|
|
222
244
|
assert desc[0].name == 'id', desc[0].name
|
|
@@ -226,74 +248,67 @@ class TestBasics(unittest.TestCase):
|
|
|
226
248
|
assert desc[2].name == 'value', desc[2].name
|
|
227
249
|
assert desc[2].type_code == 8, desc[2].type_code
|
|
228
250
|
|
|
251
|
+
with self.assertRaises(TypeError):
|
|
252
|
+
self.cur.execute(
|
|
253
|
+
'select * from data where id < %s and id > %s', ['d', 'e', 'f'],
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
with self.assertRaises(TypeError):
|
|
257
|
+
self.cur.execute('select * from data where id < %s and id > %s', ['d'])
|
|
258
|
+
|
|
229
259
|
def test_execute_with_escaped_positional_substitutions(self):
|
|
230
260
|
self.cur.execute(
|
|
231
|
-
'select `id`, `time` from alltypes where `time` =
|
|
261
|
+
'select `id`, `time` from alltypes where `time` = %s', ['00:07:00'],
|
|
232
262
|
)
|
|
233
263
|
out = self.cur.fetchall()
|
|
234
|
-
|
|
235
|
-
assert out[0] == (0, datetime.time(0, 7)), out[0]
|
|
236
|
-
else:
|
|
237
|
-
assert out[0] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
264
|
+
assert out[0] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
238
265
|
|
|
239
266
|
self.cur.execute('select `id`, `time` from alltypes where `time` = "00:07:00"')
|
|
240
267
|
out = self.cur.fetchall()
|
|
241
|
-
|
|
242
|
-
assert out[0] == (0, datetime.time(0, 7)), out[0]
|
|
243
|
-
else:
|
|
244
|
-
assert out[0] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
268
|
+
assert out[0] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
245
269
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
270
|
+
# with self.assertRaises(IndexError):
|
|
271
|
+
# self.cur.execute(
|
|
272
|
+
# 'select `id`, `time` from alltypes where `id` = %1s '
|
|
273
|
+
# 'or `time` = "00:07:00"', [0],
|
|
274
|
+
# )
|
|
251
275
|
|
|
252
276
|
self.cur.execute(
|
|
253
|
-
'select `id`, `time` from alltypes where `id` =
|
|
254
|
-
'or `time` = "00
|
|
277
|
+
'select `id`, `time` from alltypes where `id` = %s '
|
|
278
|
+
'or `time` = "00:07:00"', [0],
|
|
255
279
|
)
|
|
256
280
|
out = self.cur.fetchall()
|
|
257
|
-
|
|
258
|
-
assert out[0] == (0, datetime.time(0, 7)), out[0]
|
|
259
|
-
else:
|
|
260
|
-
assert out[0] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
281
|
+
assert out[0] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
261
282
|
|
|
262
283
|
def test_execute_with_escaped_substitutions(self):
|
|
263
284
|
self.cur.execute(
|
|
264
|
-
'select `id`, `time` from alltypes where `time` =
|
|
285
|
+
'select `id`, `time` from alltypes where `time` = %(time)s',
|
|
265
286
|
dict(time='00:07:00'),
|
|
266
287
|
)
|
|
267
288
|
out = self.cur.fetchall()
|
|
268
|
-
|
|
269
|
-
assert out[0] == (0, datetime.time(0, 7)), out[0]
|
|
270
|
-
else:
|
|
271
|
-
assert out[0] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
289
|
+
assert out[0] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
272
290
|
|
|
273
291
|
self.cur.execute(
|
|
274
|
-
'select `id`, `time` from alltypes where `time` =
|
|
275
|
-
dict(time='00
|
|
292
|
+
'select `id`, `time` from alltypes where `time` = %(time)s',
|
|
293
|
+
dict(time='00:07:00'),
|
|
276
294
|
)
|
|
277
295
|
out = self.cur.fetchall()
|
|
278
|
-
assert len(out) ==
|
|
296
|
+
assert len(out) == 1, out
|
|
279
297
|
|
|
280
298
|
with self.assertRaises(KeyError):
|
|
281
299
|
self.cur.execute(
|
|
282
300
|
'select `id`, `time`, `char_100` from alltypes '
|
|
283
|
-
'where `time` =
|
|
284
|
-
dict(
|
|
301
|
+
'where `time` = %(time)s or `char_100` like "foo:bar"',
|
|
302
|
+
dict(x='00:07:00'),
|
|
285
303
|
)
|
|
286
304
|
|
|
287
305
|
self.cur.execute(
|
|
288
306
|
'select `id`, `time`, `char_100` from alltypes '
|
|
289
|
-
'where `time` =
|
|
307
|
+
'where `time` = %(time)s or `char_100` like "foo::bar"',
|
|
290
308
|
dict(time='00:07:00'),
|
|
291
309
|
)
|
|
292
310
|
out = self.cur.fetchall()
|
|
293
|
-
|
|
294
|
-
assert out[0][:2] == (0, datetime.time(0, 7)), out[0]
|
|
295
|
-
else:
|
|
296
|
-
assert out[0][:2] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
311
|
+
assert out[0][:2] == (0, datetime.timedelta(seconds=420)), out[0]
|
|
297
312
|
|
|
298
313
|
def test_is_connected(self):
|
|
299
314
|
assert self.conn.is_connected()
|
|
@@ -313,7 +328,7 @@ class TestBasics(unittest.TestCase):
|
|
|
313
328
|
def test_executemany(self):
|
|
314
329
|
# NOTE: Doesn't actually do anything since no rows match
|
|
315
330
|
self.cur.executemany(
|
|
316
|
-
'delete from data where id >
|
|
331
|
+
'delete from data where id > %(name)s',
|
|
317
332
|
[dict(name='z'), dict(name='y')],
|
|
318
333
|
)
|
|
319
334
|
|
|
@@ -484,10 +499,147 @@ class TestBasics(unittest.TestCase):
|
|
|
484
499
|
self.cur.execute('garbage syntax')
|
|
485
500
|
|
|
486
501
|
exc = cm.exception
|
|
487
|
-
|
|
488
|
-
assert exc.errno == 1064, exc.errno
|
|
502
|
+
assert exc.errno == 1064, exc.errno
|
|
489
503
|
assert 'You have an error in your SQL syntax' in exc.errmsg, exc.errmsg
|
|
490
504
|
|
|
505
|
+
def test_extended_types(self):
|
|
506
|
+
if not has_numpy or not has_pygeos or not has_shapely:
|
|
507
|
+
self.skipTest('Test requires numpy, pygeos, and shapely')
|
|
508
|
+
|
|
509
|
+
import uuid
|
|
510
|
+
|
|
511
|
+
key = str(uuid.uuid4())
|
|
512
|
+
|
|
513
|
+
# shapely data
|
|
514
|
+
data = [
|
|
515
|
+
(
|
|
516
|
+
1, 'POLYGON((1 1, 2 1, 2 2, 1 2, 1 1))', 'POINT(1.5 1.5)',
|
|
517
|
+
[0.5, 0.6], datetime.datetime(1950, 1, 2, 12, 13, 14),
|
|
518
|
+
datetime.date(1950, 1, 2), datetime.time(12, 13, 14),
|
|
519
|
+
datetime.timedelta(seconds=123456), key,
|
|
520
|
+
),
|
|
521
|
+
(
|
|
522
|
+
2, 'POLYGON((5 1, 6 1, 6 2, 5 2, 5 1))', 'POINT(5.5 1.5)',
|
|
523
|
+
[1.3, 2.5], datetime.datetime(1960, 3, 4, 15, 16, 17),
|
|
524
|
+
datetime.date(1960, 3, 4), datetime.time(15, 16, 17),
|
|
525
|
+
datetime.timedelta(seconds=2), key,
|
|
526
|
+
),
|
|
527
|
+
(
|
|
528
|
+
3, 'POLYGON((5 5, 6 5, 6 6, 5 6, 5 5))', 'POINT(5.5 5.5)',
|
|
529
|
+
[10.3, 11.1], datetime.datetime(1970, 6, 7, 18, 19, 20),
|
|
530
|
+
datetime.date(1970, 5, 6), datetime.time(18, 19, 20),
|
|
531
|
+
datetime.timedelta(seconds=-2), key,
|
|
532
|
+
),
|
|
533
|
+
(
|
|
534
|
+
4, 'POLYGON((1 5, 2 5, 2 6, 1 6, 1 5))', 'POINT(1.5 5.5)',
|
|
535
|
+
[3.3, 3.4], datetime.datetime(1980, 8, 9, 21, 22, 23),
|
|
536
|
+
datetime.date(1980, 7, 8), datetime.time(21, 22, 23),
|
|
537
|
+
datetime.timedelta(seconds=-123456), key,
|
|
538
|
+
),
|
|
539
|
+
(
|
|
540
|
+
5, 'POLYGON((3 3, 4 3, 4 4, 3 4, 3 3))', 'POINT(3.5 3.5)',
|
|
541
|
+
[2.9, 9.5], datetime.datetime(2010, 10, 11, 1, 2, 3),
|
|
542
|
+
datetime.date(2010, 8, 9), datetime.time(1, 2, 3),
|
|
543
|
+
datetime.timedelta(seconds=0), key,
|
|
544
|
+
),
|
|
545
|
+
]
|
|
546
|
+
|
|
547
|
+
new_data = []
|
|
548
|
+
for i, row in enumerate(data):
|
|
549
|
+
row = list(row)
|
|
550
|
+
row[1] = shapely.wkt.loads(row[1])
|
|
551
|
+
row[2] = shapely.wkt.loads(row[2])
|
|
552
|
+
if 'http' in self.conn.driver:
|
|
553
|
+
row[3] = ''
|
|
554
|
+
else:
|
|
555
|
+
row[3] = np.array(row[3], dtype='<f4')
|
|
556
|
+
new_data.append(row)
|
|
557
|
+
|
|
558
|
+
self.cur.executemany(
|
|
559
|
+
'INSERT INTO extended_types '
|
|
560
|
+
'(id, geography, geographypoint, vectors, dt, d, t, td, testkey) '
|
|
561
|
+
'VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)', new_data,
|
|
562
|
+
)
|
|
563
|
+
|
|
564
|
+
self.cur.execute(
|
|
565
|
+
'SELECT * FROM extended_types WHERE testkey = %s ORDER BY id', [key],
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
for data_row, row in zip(new_data, self.cur):
|
|
569
|
+
assert data_row[0] == row[0]
|
|
570
|
+
assert data_row[1].equals_exact(shapely.wkt.loads(row[1]), 1e-4)
|
|
571
|
+
assert data_row[2].equals_exact(shapely.wkt.loads(row[2]), 1e-4)
|
|
572
|
+
if 'http' in self.conn.driver:
|
|
573
|
+
assert row[3] == b''
|
|
574
|
+
else:
|
|
575
|
+
assert (data_row[3] == np.frombuffer(row[3], dtype='<f4')).all()
|
|
576
|
+
|
|
577
|
+
# pygeos data
|
|
578
|
+
data = [
|
|
579
|
+
(
|
|
580
|
+
6, 'POLYGON((1 1, 2 1, 2 2, 1 2, 1 1))', 'POINT(1.5 1.5)',
|
|
581
|
+
[0.5, 0.6], datetime.datetime(1950, 1, 2, 12, 13, 14),
|
|
582
|
+
datetime.date(1950, 1, 2), datetime.time(12, 13, 14),
|
|
583
|
+
datetime.timedelta(seconds=123456), key,
|
|
584
|
+
),
|
|
585
|
+
(
|
|
586
|
+
7, 'POLYGON((5 1, 6 1, 6 2, 5 2, 5 1))', 'POINT(5.5 1.5)',
|
|
587
|
+
[1.3, 2.5], datetime.datetime(1960, 3, 4, 15, 16, 17),
|
|
588
|
+
datetime.date(1960, 3, 4), datetime.time(15, 16, 17),
|
|
589
|
+
datetime.timedelta(seconds=2), key,
|
|
590
|
+
),
|
|
591
|
+
(
|
|
592
|
+
8, 'POLYGON((5 5, 6 5, 6 6, 5 6, 5 5))', 'POINT(5.5 5.5)',
|
|
593
|
+
[10.3, 11.1], datetime.datetime(1970, 6, 7, 18, 19, 20),
|
|
594
|
+
datetime.date(1970, 5, 6), datetime.time(18, 19, 20),
|
|
595
|
+
datetime.timedelta(seconds=-2), key,
|
|
596
|
+
),
|
|
597
|
+
(
|
|
598
|
+
9, 'POLYGON((1 5, 2 5, 2 6, 1 6, 1 5))', 'POINT(1.5 5.5)',
|
|
599
|
+
[3.3, 3.4], datetime.datetime(1980, 8, 9, 21, 22, 23),
|
|
600
|
+
datetime.date(1980, 7, 8), datetime.time(21, 22, 23),
|
|
601
|
+
datetime.timedelta(seconds=-123456), key,
|
|
602
|
+
),
|
|
603
|
+
(
|
|
604
|
+
10, 'POLYGON((3 3, 4 3, 4 4, 3 4, 3 3))', 'POINT(3.5 3.5)',
|
|
605
|
+
[2.9, 9.5], datetime.datetime(2010, 10, 11, 1, 2, 3),
|
|
606
|
+
datetime.date(2010, 8, 9), datetime.time(1, 2, 3),
|
|
607
|
+
datetime.timedelta(seconds=0), key,
|
|
608
|
+
),
|
|
609
|
+
]
|
|
610
|
+
|
|
611
|
+
new_data = []
|
|
612
|
+
for i, row in enumerate(data):
|
|
613
|
+
row = list(row)
|
|
614
|
+
row[1] = pygeos.io.from_wkt(row[1])
|
|
615
|
+
row[2] = pygeos.io.from_wkt(row[2])
|
|
616
|
+
if 'http' in self.conn.driver:
|
|
617
|
+
row[3] = ''
|
|
618
|
+
else:
|
|
619
|
+
row[3] = np.array(row[3], dtype='<f4')
|
|
620
|
+
new_data.append(row)
|
|
621
|
+
|
|
622
|
+
self.cur.executemany(
|
|
623
|
+
'INSERT INTO extended_types '
|
|
624
|
+
'(id, geography, geographypoint, vectors, dt, d, t, td, testkey) '
|
|
625
|
+
'VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s)', new_data,
|
|
626
|
+
)
|
|
627
|
+
|
|
628
|
+
self.cur.execute(
|
|
629
|
+
'SELECT * FROM extended_types WHERE id >= 6 and testkey = %s ORDER BY id', [
|
|
630
|
+
key,
|
|
631
|
+
],
|
|
632
|
+
)
|
|
633
|
+
|
|
634
|
+
for data_row, row in zip(new_data, self.cur):
|
|
635
|
+
assert data_row[0] == row[0]
|
|
636
|
+
assert_geometries_equal(data_row[1], pygeos.io.from_wkt(row[1]))
|
|
637
|
+
assert_geometries_equal(data_row[2], pygeos.io.from_wkt(row[2]))
|
|
638
|
+
if 'http' in self.conn.driver:
|
|
639
|
+
assert row[3] == b''
|
|
640
|
+
else:
|
|
641
|
+
assert (data_row[3] == np.frombuffer(row[3], dtype='<f4')).all()
|
|
642
|
+
|
|
491
643
|
def test_alltypes(self):
|
|
492
644
|
self.cur.execute('select * from alltypes where id = 0')
|
|
493
645
|
names = [x[0] for x in self.cur.description]
|
|
@@ -498,30 +650,8 @@ class TestBasics(unittest.TestCase):
|
|
|
498
650
|
|
|
499
651
|
bits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
|
|
500
652
|
|
|
501
|
-
if self.driver == 'pyodbc':
|
|
502
|
-
odbc_types = {
|
|
503
|
-
# int -> bigint
|
|
504
|
-
3: 8, 1: 8, 2: 8, 9: 8,
|
|
505
|
-
# float -> double
|
|
506
|
-
4: 5,
|
|
507
|
-
# timestamp -> datetime
|
|
508
|
-
7: 12,
|
|
509
|
-
# year -> bigint
|
|
510
|
-
13: 8,
|
|
511
|
-
# char/binary -> varchar/varbinary
|
|
512
|
-
249: 15, 250: 15, 251: 15, 252: 15, 253: 15, 254: 15, 255: 15,
|
|
513
|
-
# newdecimal -> decimal
|
|
514
|
-
246: 0,
|
|
515
|
-
# json -> varchar
|
|
516
|
-
245: 15,
|
|
517
|
-
# bit -> varchar
|
|
518
|
-
16: 15,
|
|
519
|
-
}
|
|
520
|
-
else:
|
|
521
|
-
odbc_types = {}
|
|
522
|
-
|
|
523
653
|
def otype(x):
|
|
524
|
-
return
|
|
654
|
+
return x
|
|
525
655
|
|
|
526
656
|
assert row['id'] == 0, row['id']
|
|
527
657
|
assert typ['id'] == otype(3), typ['id']
|
|
@@ -577,21 +707,12 @@ class TestBasics(unittest.TestCase):
|
|
|
577
707
|
assert row['date'] == datetime.date(8524, 11, 10), row['date']
|
|
578
708
|
assert typ['date'] == 10, typ['date']
|
|
579
709
|
|
|
580
|
-
|
|
581
|
-
# put your own converter in, it changes the type code to 15!!!
|
|
582
|
-
if self.driver == 'pyodbc':
|
|
583
|
-
assert row['time'] == datetime.time(0, 7, 0), row['time']
|
|
584
|
-
else:
|
|
585
|
-
assert row['time'] == datetime.timedelta(minutes=7), row['time']
|
|
710
|
+
assert row['time'] == datetime.timedelta(minutes=7), row['time']
|
|
586
711
|
assert typ['time'] == 11, typ['time']
|
|
587
712
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
else:
|
|
592
|
-
assert row['time_6'] == datetime.timedelta(
|
|
593
|
-
hours=1, minutes=10, microseconds=2,
|
|
594
|
-
), row['time_6']
|
|
713
|
+
assert row['time_6'] == datetime.timedelta(
|
|
714
|
+
hours=1, minutes=10, microseconds=2,
|
|
715
|
+
), row['time_6']
|
|
595
716
|
assert typ['time_6'] == 11, typ['time_6']
|
|
596
717
|
|
|
597
718
|
assert row['datetime'] == datetime.datetime(
|
|
@@ -655,11 +776,7 @@ class TestBasics(unittest.TestCase):
|
|
|
655
776
|
assert row['tinyblob'] == bytearray([10, 11, 12, 13, 14, 15]), row['tinyblob']
|
|
656
777
|
assert typ['tinyblob'] == otype(249), typ['tinyblob']
|
|
657
778
|
|
|
658
|
-
|
|
659
|
-
if self.driver == 'pyodbc':
|
|
660
|
-
assert row['json'] == '{"a":10,"b":2.75,"c":"hello world"}', row['json']
|
|
661
|
-
else:
|
|
662
|
-
assert row['json'] == {'a': 10, 'b': 2.75, 'c': 'hello world'}, row['json']
|
|
779
|
+
assert row['json'] == {'a': 10, 'b': 2.75, 'c': 'hello world'}, row['json']
|
|
663
780
|
assert typ['json'] == otype(245), typ['json']
|
|
664
781
|
|
|
665
782
|
assert row['enum'] == 'one', row['enum']
|
|
@@ -669,11 +786,7 @@ class TestBasics(unittest.TestCase):
|
|
|
669
786
|
assert row['set'] in [{'two'}, 'two'], row['set']
|
|
670
787
|
assert typ['set'] == otype(253), typ['set'] # mysql code: 248
|
|
671
788
|
|
|
672
|
-
|
|
673
|
-
if self.driver == 'pyodbc':
|
|
674
|
-
assert row['bit'] == b'\x80\x00\x00\x00\x00\x00\x00\x00', row['bit']
|
|
675
|
-
else:
|
|
676
|
-
assert row['bit'] == b'\x00\x00\x00\x00\x00\x00\x00\x80', row['bit']
|
|
789
|
+
assert row['bit'] == b'\x00\x00\x00\x00\x00\x00\x00\x80', row['bit']
|
|
677
790
|
assert typ['bit'] == otype(16), typ['bit']
|
|
678
791
|
|
|
679
792
|
def test_alltypes_nulls(self):
|
|
@@ -684,30 +797,8 @@ class TestBasics(unittest.TestCase):
|
|
|
684
797
|
row = dict(zip(names, out))
|
|
685
798
|
typ = dict(zip(names, types))
|
|
686
799
|
|
|
687
|
-
if self.driver == 'pyodbc':
|
|
688
|
-
odbc_types = {
|
|
689
|
-
# int -> bigint
|
|
690
|
-
3: 8, 1: 8, 2: 8, 9: 8,
|
|
691
|
-
# float -> double
|
|
692
|
-
4: 5,
|
|
693
|
-
# timestamp -> datetime
|
|
694
|
-
7: 12,
|
|
695
|
-
# year -> bigint
|
|
696
|
-
13: 8,
|
|
697
|
-
# char/binary -> varchar/varbinary
|
|
698
|
-
249: 15, 250: 15, 251: 15, 252: 15, 253: 15, 254: 15, 255: 15,
|
|
699
|
-
# newdecimal -> decimal
|
|
700
|
-
246: 0,
|
|
701
|
-
# json -> varchar
|
|
702
|
-
245: 15,
|
|
703
|
-
# bit -> varchar
|
|
704
|
-
16: 15,
|
|
705
|
-
}
|
|
706
|
-
else:
|
|
707
|
-
odbc_types = {}
|
|
708
|
-
|
|
709
800
|
def otype(x):
|
|
710
|
-
return
|
|
801
|
+
return x
|
|
711
802
|
|
|
712
803
|
assert row['id'] == 1, row['id']
|
|
713
804
|
assert typ['id'] == otype(3), typ['id']
|
|
@@ -832,6 +923,280 @@ class TestBasics(unittest.TestCase):
|
|
|
832
923
|
assert row['bit'] is None, row['bit']
|
|
833
924
|
assert typ['bit'] == otype(16), typ['bit']
|
|
834
925
|
|
|
926
|
+
def test_alltypes_mins(self):
|
|
927
|
+
self.cur.execute('select * from alltypes where id = 2')
|
|
928
|
+
names = [x[0] for x in self.cur.description]
|
|
929
|
+
out = self.cur.fetchone()
|
|
930
|
+
row = dict(zip(names, out))
|
|
931
|
+
|
|
932
|
+
expected = dict(
|
|
933
|
+
id=2,
|
|
934
|
+
tinyint=-128,
|
|
935
|
+
unsigned_tinyint=0,
|
|
936
|
+
bool=-128,
|
|
937
|
+
boolean=-128,
|
|
938
|
+
smallint=-32768,
|
|
939
|
+
unsigned_smallint=0,
|
|
940
|
+
mediumint=-8388608,
|
|
941
|
+
unsigned_mediumint=0,
|
|
942
|
+
int24=-8388608,
|
|
943
|
+
unsigned_int24=0,
|
|
944
|
+
int=-2147483648,
|
|
945
|
+
unsigned_int=0,
|
|
946
|
+
integer=-2147483648,
|
|
947
|
+
unsigned_integer=0,
|
|
948
|
+
bigint=-9223372036854775808,
|
|
949
|
+
unsigned_bigint=0,
|
|
950
|
+
float=0,
|
|
951
|
+
double=-1.7976931348623158e308,
|
|
952
|
+
real=-1.7976931348623158e308,
|
|
953
|
+
decimal=decimal.Decimal('-99999999999999.999999'),
|
|
954
|
+
dec=-decimal.Decimal('99999999999999.999999'),
|
|
955
|
+
fixed=decimal.Decimal('-99999999999999.999999'),
|
|
956
|
+
numeric=decimal.Decimal('-99999999999999.999999'),
|
|
957
|
+
date=datetime.date(1000, 1, 1),
|
|
958
|
+
time=-1 * datetime.timedelta(hours=838, minutes=59, seconds=59),
|
|
959
|
+
time_6=-1 * datetime.timedelta(hours=838, minutes=59, seconds=59),
|
|
960
|
+
datetime=datetime.datetime(1000, 1, 1, 0, 0, 0),
|
|
961
|
+
datetime_6=datetime.datetime(1000, 1, 1, 0, 0, 0, 0),
|
|
962
|
+
timestamp=datetime.datetime(1970, 1, 1, 0, 0, 1),
|
|
963
|
+
timestamp_6=datetime.datetime(1970, 1, 1, 0, 0, 1, 0),
|
|
964
|
+
year=1901,
|
|
965
|
+
char_100='',
|
|
966
|
+
binary_100=b'\x00' * 100,
|
|
967
|
+
varchar_200='',
|
|
968
|
+
varbinary_200=b'',
|
|
969
|
+
longtext='',
|
|
970
|
+
mediumtext='',
|
|
971
|
+
text='',
|
|
972
|
+
tinytext='',
|
|
973
|
+
longblob=b'',
|
|
974
|
+
mediumblob=b'',
|
|
975
|
+
blob=b'',
|
|
976
|
+
tinyblob=b'',
|
|
977
|
+
json={},
|
|
978
|
+
enum='one',
|
|
979
|
+
set='two',
|
|
980
|
+
bit=b'\x00\x00\x00\x00\x00\x00\x00\x00',
|
|
981
|
+
)
|
|
982
|
+
|
|
983
|
+
for k, v in sorted(row.items()):
|
|
984
|
+
assert v == expected[k], '{} != {} in key {}'.format(v, expected[k], k)
|
|
985
|
+
|
|
986
|
+
def test_alltypes_maxs(self):
|
|
987
|
+
self.cur.execute('select * from alltypes where id = 3')
|
|
988
|
+
names = [x[0] for x in self.cur.description]
|
|
989
|
+
out = self.cur.fetchone()
|
|
990
|
+
row = dict(zip(names, out))
|
|
991
|
+
|
|
992
|
+
expected = dict(
|
|
993
|
+
id=3,
|
|
994
|
+
tinyint=127,
|
|
995
|
+
unsigned_tinyint=255,
|
|
996
|
+
bool=127,
|
|
997
|
+
boolean=127,
|
|
998
|
+
smallint=32767,
|
|
999
|
+
unsigned_smallint=65535,
|
|
1000
|
+
mediumint=8388607,
|
|
1001
|
+
unsigned_mediumint=16777215,
|
|
1002
|
+
int24=8388607,
|
|
1003
|
+
unsigned_int24=16777215,
|
|
1004
|
+
int=2147483647,
|
|
1005
|
+
unsigned_int=4294967295,
|
|
1006
|
+
integer=2147483647,
|
|
1007
|
+
unsigned_integer=4294967295,
|
|
1008
|
+
bigint=9223372036854775807,
|
|
1009
|
+
unsigned_bigint=18446744073709551615,
|
|
1010
|
+
float=0,
|
|
1011
|
+
double=1.7976931348623158e308,
|
|
1012
|
+
real=1.7976931348623158e308,
|
|
1013
|
+
decimal=decimal.Decimal('99999999999999.999999'),
|
|
1014
|
+
dec=decimal.Decimal('99999999999999.999999'),
|
|
1015
|
+
fixed=decimal.Decimal('99999999999999.999999'),
|
|
1016
|
+
numeric=decimal.Decimal('99999999999999.999999'),
|
|
1017
|
+
date=datetime.date(9999, 12, 31),
|
|
1018
|
+
time=datetime.timedelta(hours=838, minutes=59, seconds=59),
|
|
1019
|
+
time_6=datetime.timedelta(hours=838, minutes=59, seconds=59),
|
|
1020
|
+
datetime=datetime.datetime(9999, 12, 31, 23, 59, 59),
|
|
1021
|
+
datetime_6=datetime.datetime(9999, 12, 31, 23, 59, 59, 999999),
|
|
1022
|
+
timestamp=datetime.datetime(2038, 1, 19, 3, 14, 7),
|
|
1023
|
+
timestamp_6=datetime.datetime(2038, 1, 19, 3, 14, 7, 999999),
|
|
1024
|
+
year=2155,
|
|
1025
|
+
char_100='',
|
|
1026
|
+
binary_100=b'\x00' * 100,
|
|
1027
|
+
varchar_200='',
|
|
1028
|
+
varbinary_200=b'',
|
|
1029
|
+
longtext='',
|
|
1030
|
+
mediumtext='',
|
|
1031
|
+
text='',
|
|
1032
|
+
tinytext='',
|
|
1033
|
+
longblob=b'',
|
|
1034
|
+
mediumblob=b'',
|
|
1035
|
+
blob=b'',
|
|
1036
|
+
tinyblob=b'',
|
|
1037
|
+
json={},
|
|
1038
|
+
enum='one',
|
|
1039
|
+
set='two',
|
|
1040
|
+
bit=b'\xff\xff\xff\xff\xff\xff\xff\xff',
|
|
1041
|
+
)
|
|
1042
|
+
|
|
1043
|
+
for k, v in sorted(row.items()):
|
|
1044
|
+
# TODO: Figure out how to get time zones working
|
|
1045
|
+
if 'timestamp' in k:
|
|
1046
|
+
continue
|
|
1047
|
+
assert v == expected[k], '{} != {} in key {}'.format(v, expected[k], k)
|
|
1048
|
+
|
|
1049
|
+
def test_alltypes_zeros(self):
|
|
1050
|
+
self.cur.execute('select * from alltypes where id = 4')
|
|
1051
|
+
names = [x[0] for x in self.cur.description]
|
|
1052
|
+
out = self.cur.fetchone()
|
|
1053
|
+
row = dict(zip(names, out))
|
|
1054
|
+
|
|
1055
|
+
expected = dict(
|
|
1056
|
+
id=4,
|
|
1057
|
+
tinyint=0,
|
|
1058
|
+
unsigned_tinyint=0,
|
|
1059
|
+
bool=0,
|
|
1060
|
+
boolean=0,
|
|
1061
|
+
smallint=0,
|
|
1062
|
+
unsigned_smallint=0,
|
|
1063
|
+
mediumint=0,
|
|
1064
|
+
unsigned_mediumint=0,
|
|
1065
|
+
int24=0,
|
|
1066
|
+
unsigned_int24=0,
|
|
1067
|
+
int=0,
|
|
1068
|
+
unsigned_int=0,
|
|
1069
|
+
integer=0,
|
|
1070
|
+
unsigned_integer=0,
|
|
1071
|
+
bigint=0,
|
|
1072
|
+
unsigned_bigint=0,
|
|
1073
|
+
float=0,
|
|
1074
|
+
double=0,
|
|
1075
|
+
real=0,
|
|
1076
|
+
decimal=decimal.Decimal('0.0'),
|
|
1077
|
+
dec=decimal.Decimal('0.0'),
|
|
1078
|
+
fixed=decimal.Decimal('0.0'),
|
|
1079
|
+
numeric=decimal.Decimal('0.0'),
|
|
1080
|
+
date=None,
|
|
1081
|
+
time=datetime.timedelta(hours=0, minutes=0, seconds=0),
|
|
1082
|
+
time_6=datetime.timedelta(hours=0, minutes=0, seconds=0, microseconds=0),
|
|
1083
|
+
datetime=None,
|
|
1084
|
+
datetime_6=None,
|
|
1085
|
+
timestamp=None,
|
|
1086
|
+
timestamp_6=None,
|
|
1087
|
+
year=None,
|
|
1088
|
+
char_100='',
|
|
1089
|
+
binary_100=b'\x00' * 100,
|
|
1090
|
+
varchar_200='',
|
|
1091
|
+
varbinary_200=b'',
|
|
1092
|
+
longtext='',
|
|
1093
|
+
mediumtext='',
|
|
1094
|
+
text='',
|
|
1095
|
+
tinytext='',
|
|
1096
|
+
longblob=b'',
|
|
1097
|
+
mediumblob=b'',
|
|
1098
|
+
blob=b'',
|
|
1099
|
+
tinyblob=b'',
|
|
1100
|
+
json={},
|
|
1101
|
+
enum='one',
|
|
1102
|
+
set='two',
|
|
1103
|
+
bit=b'\x00\x00\x00\x00\x00\x00\x00\x00',
|
|
1104
|
+
)
|
|
1105
|
+
|
|
1106
|
+
for k, v in sorted(row.items()):
|
|
1107
|
+
assert v == expected[k], '{} != {} in key {}'.format(v, expected[k], k)
|
|
1108
|
+
|
|
1109
|
+
def _test_MySQLdb(self):
|
|
1110
|
+
try:
|
|
1111
|
+
import json
|
|
1112
|
+
import MySQLdb
|
|
1113
|
+
except (ModuleNotFoundError, ImportError):
|
|
1114
|
+
self.skipTest('MySQLdb is not installed')
|
|
1115
|
+
|
|
1116
|
+
self.cur.execute('select * from alltypes order by id')
|
|
1117
|
+
s2_out = self.cur.fetchall()
|
|
1118
|
+
|
|
1119
|
+
port = self.conn.connection_params['port']
|
|
1120
|
+
if 'http' in self.conn.driver:
|
|
1121
|
+
port = 3306
|
|
1122
|
+
|
|
1123
|
+
args = dict(
|
|
1124
|
+
host=self.conn.connection_params['host'],
|
|
1125
|
+
port=port,
|
|
1126
|
+
user=self.conn.connection_params['user'],
|
|
1127
|
+
password=self.conn.connection_params['password'],
|
|
1128
|
+
database=type(self).dbname,
|
|
1129
|
+
)
|
|
1130
|
+
|
|
1131
|
+
with MySQLdb.connect(**args) as conn:
|
|
1132
|
+
conn.converter[245] = json.loads
|
|
1133
|
+
with conn.cursor() as cur:
|
|
1134
|
+
cur.execute('select * from alltypes order by id')
|
|
1135
|
+
mydb_out = cur.fetchall()
|
|
1136
|
+
|
|
1137
|
+
for a, b in zip(s2_out, mydb_out):
|
|
1138
|
+
assert a == b, (a, b)
|
|
1139
|
+
|
|
1140
|
+
def test_int_string(self):
|
|
1141
|
+
string = 'a' * 48
|
|
1142
|
+
self.cur.execute(f"SELECT 1, '{string}'")
|
|
1143
|
+
self.assertEqual((1, string), self.cur.fetchone())
|
|
1144
|
+
|
|
1145
|
+
def test_double_string(self):
|
|
1146
|
+
string = 'a' * 49
|
|
1147
|
+
self.cur.execute(f"SELECT 1.2 :> DOUBLE, '{string}'")
|
|
1148
|
+
self.assertEqual((1.2, string), self.cur.fetchone())
|
|
1149
|
+
|
|
1150
|
+
def test_year_string(self):
|
|
1151
|
+
string = 'a' * 49
|
|
1152
|
+
self.cur.execute(f"SELECT 1999 :> YEAR, '{string}'")
|
|
1153
|
+
self.assertEqual((1999, string), self.cur.fetchone())
|
|
1154
|
+
|
|
1155
|
+
def test_nan_as_null(self):
|
|
1156
|
+
with self.assertRaises((s2.ProgrammingError, InvalidJSONError)):
|
|
1157
|
+
self.cur.execute('SELECT %s :> DOUBLE AS X', [math.nan])
|
|
1158
|
+
|
|
1159
|
+
with s2.connect(database=type(self).dbname, nan_as_null=True) as conn:
|
|
1160
|
+
with conn.cursor() as cur:
|
|
1161
|
+
cur.execute('SELECT %s :> DOUBLE AS X', [math.nan])
|
|
1162
|
+
self.assertEqual(None, list(cur)[0][0])
|
|
1163
|
+
|
|
1164
|
+
with s2.connect(database=type(self).dbname, nan_as_null=True) as conn:
|
|
1165
|
+
with conn.cursor() as cur:
|
|
1166
|
+
cur.execute('SELECT %s :> DOUBLE AS X', [1.234])
|
|
1167
|
+
self.assertEqual(1.234, list(cur)[0][0])
|
|
1168
|
+
|
|
1169
|
+
def test_inf_as_null(self):
|
|
1170
|
+
with self.assertRaises((s2.ProgrammingError, InvalidJSONError)):
|
|
1171
|
+
self.cur.execute('SELECT %s :> DOUBLE AS X', [math.inf])
|
|
1172
|
+
|
|
1173
|
+
with s2.connect(database=type(self).dbname, inf_as_null=True) as conn:
|
|
1174
|
+
with conn.cursor() as cur:
|
|
1175
|
+
cur.execute('SELECT %s :> DOUBLE AS X', [math.inf])
|
|
1176
|
+
self.assertEqual(None, list(cur)[0][0])
|
|
1177
|
+
|
|
1178
|
+
with s2.connect(database=type(self).dbname, inf_as_null=True) as conn:
|
|
1179
|
+
with conn.cursor() as cur:
|
|
1180
|
+
cur.execute('SELECT %s :> DOUBLE AS X', [1.234])
|
|
1181
|
+
self.assertEqual(1.234, list(cur)[0][0])
|
|
1182
|
+
|
|
1183
|
+
def test_encoding_errors(self):
|
|
1184
|
+
with s2.connect(
|
|
1185
|
+
database=type(self).dbname,
|
|
1186
|
+
encoding_errors='strict',
|
|
1187
|
+
) as conn:
|
|
1188
|
+
with conn.cursor() as cur:
|
|
1189
|
+
cur.execute('SELECT * FROM badutf8')
|
|
1190
|
+
list(cur)
|
|
1191
|
+
|
|
1192
|
+
with s2.connect(
|
|
1193
|
+
database=type(self).dbname,
|
|
1194
|
+
encoding_errors='backslashreplace',
|
|
1195
|
+
) as conn:
|
|
1196
|
+
with conn.cursor() as cur:
|
|
1197
|
+
cur.execute('SELECT * FROM badutf8')
|
|
1198
|
+
list(cur)
|
|
1199
|
+
|
|
835
1200
|
|
|
836
1201
|
if __name__ == '__main__':
|
|
837
1202
|
import nose2
|